/*
 * Copyright (c) 2011-2014, fortiss GmbH.
 * Licensed under the Apache License, Version 2.0.
 *
 * Use, modification and distribution are subject to the terms specified
 * in the accompanying license file LICENSE.txt located at the root directory
 * of this software distribution. A copy is available at
 * http://chromosome.fortiss.org/.
 *
 * This file is part of CHROMOSOME.
 *
 * $Id: logicalRouteManager.c 7829 2014-03-14 10:29:33Z ruiz $
 */

/**
 * \file
 *         Logical Route Manager.
 */

/**
 * \addtogroup core_pnp_lrm
 * @{
 */

/******************************************************************************/
/***   Includes                                                             ***/
/******************************************************************************/
#include "xme/core/plugAndPlay/include/logicalRouteManager.h"

#include "xme/core/plugAndPlay/include/dataLinkGraph.h"

#include "xme/hal/include/mem.h"

/******************************************************************************/
/***   Type Definitions                                                     ***/
/******************************************************************************/

/**
 * \struct xme_core_pnp_lrm_state_t
 *
 * \brief Represents the internal state of the logical route manager. 
 * \details This data is used internally to store the corresponding routes, publications
 *          and subscriptions. Additionally, it is stored the maximum transaction id and
 *          the maximum channel id. 
 */
typedef struct 
{
    bool initialized; ///< determines if the state is initialized. 
    // TODO: Move static size to Options.cmake
    xme_hal_singlyLinkedList_t(10) graphs; ///< a linked list of state subgraphs. 
    xme_core_transactionId_t maxTransactionId; ///< the maximum assigned transaction id. 
    xme_core_channelId_t maxChannelID; ///< the maximum assigned channel id. 
} xme_core_pnp_lrm_state_t;

/**
 * \struct xme_core_pnp_lrm_stateSubGraph_t
 *
 * \brief This subgraph stores all internally generated graphs.
 * \details This includes, at least the topic id, publications, subscriptions,
 *          generated routes, and associated transaction id. 
 */
typedef struct
{
    xme_core_topic_t topicId; ///< the topic id. 
    xme_hal_linkedList_descriptor_t publications; ///< the linked list containing the set of publications. 
    xme_hal_linkedList_descriptor_t subscriptions; ///< the linked list containing the set of subscriptions. 
    xme_core_pnp_lrm_logicalRoutes_t routes; ///< the generated routes. 
    xme_core_transactionId_t transactionId; ///< the associated transaction id. 
} xme_core_pnp_lrm_stateSubGraph_t;

/**
 * \struct xme_core_pnp_lrm_requestResponseMapping_t
 *
 * \brief This data structure stores the temporary data needed for create the mapping between request and response. 
 */
typedef struct
{
    xme_hal_graph_vertexId_t requestHandlerVertexID; ///< request handler vertex identifier. 
    xme_hal_graph_vertexId_t responseSenderVertexID; ///< response sender vertex identifier. 
    xme_core_channelId_t requestChannelID; ///< the request channel ID. 
    xme_core_channelId_t responseChannelID; ///< the response channel ID. 
    xme_core_component_t requestSenderComponentID; ///< the request sender component identifier. 
    xme_core_component_t requestResponseComponentID; ///< the request response component identifier. 
    xme_core_component_t responseHandlerComponentID; ///< the response handler component identifier. 
    xme_core_node_nodeId_t requestSenderNodeID; ///< the request sender node identier. 
    xme_core_node_nodeId_t requestResponseNodeID; ///< the request response node identifier. 
    xme_core_node_nodeId_t responseHandlerNodeID; ///< the response handler node identier. 
} xme_core_pnp_lrm_requestResponseMapping_t;

xme_hal_singlyLinkedList_t(0) temporaryMapping; ///< This temporary mapping is used to store information for mapping requests and responses. 

/**
 * \struct publication_subscription_channel_mappings_t
 *
 * \brief Represents the channel ID associated with a given publication/subscription pair.
 *
 * \details This data is used internally to store the channel IDs generated by the static route
 *          calculation by XMT.
 */
typedef struct
{
    xme_core_node_nodeId_t publicationNodeID; ///< Node ID of the publisher port.
    xme_core_component_t publicationComponentID; ///< Component ID for the publisher port.
    uint16_t publicationPortIndex; ///< Portindex of the publisher port for that component.
    xme_core_node_nodeId_t subscriptionNodeID; ///< Node ID of the subscriber port.
    xme_core_component_t subscriptionComponentID; ///< Component ID for the subscriber port.
    uint16_t subscriptionPortIndex; ///< Portindex of the subscriber port for that component.
    xme_core_channelId_t channelID; ///< The channel identifier.
} publication_subscription_channel_mappings_t;


/******************************************************************************/
/***   Variables                                                            ***/
/******************************************************************************/

/**
 * \brief The static logial route manager state. 
 */
static xme_core_pnp_lrm_state_t xme_core_pnp_lrm_state;

/**
 * \brief Table to store the channel ID associated with a given publication/subscription pair.
 */
static XME_HAL_TABLE(publication_subscription_channel_mappings_t, publication_subscription_channel_mappings_table, XME_CORE_PNP_LRM_MAX_CHANNELID_MAPPINGS);

/******************************************************************************/
/***   Prototypes                                                           ***/
/******************************************************************************/

/**
 * \brief Creates a new state subgraph. 
 *
 * \param[out] subGraph the generated empty subgraph. 
 *
 * \retval XME_STATUS_SUCCESS if the subgraph is successfully generated. 
 * \retval XME_STATUS_OUT_OF_RESOURCES if the subgraph cannot be generated in the 
 *         logical route manager.
 */
xme_status_t 
xme_core_pnp_lrm_createSubGraph
(
    xme_core_pnp_lrm_stateSubGraph_t** subGraph
);

/**
 * \brief Frees resources from a state subgraph. 
 *
 * \param subGraph the state subgraph to be freed. 
 *
 * \retval XME_STATUS_SUCCESS if subgraph resources has been sucessfully freed. 
 * \retval XME_STATUS_OUT_OF_RESOURCES if the subgraph cannot be freed in the 
 *         logical route manager.
 */
xme_status_t 
xme_core_pnp_lrm_removeSubGraph
(
    xme_core_pnp_lrm_stateSubGraph_t** subGraph
);

#if 0
/**
 * \brief Boolean function that determine if a given announcement already exists. 
 *
 * \param[in] announcementList the list of internal announcements already published. 
 * \param[in] announcement the announcement to compare. 
 *
 * \retval true if the announcement is already part of the announcement list. 
 * \retval false if the announcement does not exist on the announcement list.
 */
bool xme_core_pnp_lrm_announcementExists
(
    xme_hal_linkedList_descriptor_t announcementList, 
    const xme_core_pnp_lrm_announcement_t* const announcement
);
#endif

/**
 * \brief A comparison function to compare two announcements.
 *
 * \param announcement1 the first announcement to compare. 
 * \param announcement2 the second announcement to compare. 
 *
 * \return an integer value stating if the first announcement is
 *         lower, equal or greater than the second announcement. 
 */
int 
xme_core_pnp_lrm_compareAnnouncement
(
    xme_core_pnp_lrm_announcement_t* announcement1, 
    xme_core_pnp_lrm_announcement_t* announcement2
);

/**
 * \brief Adds a component port vertex to a given graph. 
 *
 * \param graph The graph in which is inserted the component port vertex. 
 * \param componentPortVertex the component port vertex to insert in the graph. 
 *
 * \retval XME_STATUS_SUCCESS if the component port vertex has been sucessfully added
 *         to the graph. 
 * \retval XME_STATUS_INVALID_PARAMETER if input parameters are not valid.
 * \retval XME_STATUS_OUT_OF_RESOURCES if cannot add the component port vertex to the
 *         graph due to lack of resources. 
 * \retval XME_STATUS_INTERNAL_ERROR if cannot generate a valid component port vertex
 *         to the graph. 
 */
xme_status_t 
xme_core_pnp_lrm_addComponentPortVertex
(
    xme_hal_graph_graph_t* graph, 
    xme_core_pnp_dataLinkGraph_vertexData_t** componentPortVertex
);

#if 0
/**
 * \brief Removes a component port vertex from the given graph. 
 *
 * \param graph the graph. 
 * \param vertexId the vertex identifier to remove. 
 *
 * \retval XME_STATUS_SUCCESS if the vertex has been removed from the graph. 
 * \retval XME_STATUS_INVALID_PARAMETER if input parameters are invalid. 
 * \retval XME_STATUS_INTERNAL_ERROR if cannot remove the given vertex from the graph. 
 */
xme_status_t 
xme_core_pnp_lrm_removeComponentPortVertex
(
    xme_hal_graph_graph_t* graph, 
    xme_hal_graph_vertexId_t vertexId
);
#endif

/**
 * \brief Function that implements the comparison of two vertices
 *        in a given graph. 
 * \note Comparison affects only at component port level. 
 *
 * \param vertexData1 the first vertex data to compare. 
 * \param vertexData2 the second vertex data to compare. 
 *
 * \return an int value stating that the first vertex data is
 *         lower, equal or greater than the second vertex data. 
 */
int
xme_core_pnp_lrm_vertexCompareCallbackSameComponentPort
(
    void* vertexData1,
    void* vertexData2
);

/**
 * \brief Implementation of xme_hal_graph_edgeCompareCallback_t.
 *        Checks if the first given edge data is a logical route edge.
 *
 * \param edgeData1 the first edge data to compare. 
 * \param edgeData2 the second edge data to compare. Must be NULL.
 *
 * \return Returns 0 (equal) when edgeData1 is a logical route edge and
 *         -1 (smaller) otherwise.
 */
int
xme_core_pnp_lrm_edgeCompareCallbackLogicalRouteFilter
(
    void* edgeData1,
    void* edgeData2
);

/**
 * \brief Obtains the first logical route edge data where source and
 *        sink are the same as provided input parameters. 
 *
 * \param[in] sourceVertexData the source vertex data. 
 * \param[in] sinkVertexData the sink vertex data. 
 *
 * \return the edge data of a data link graph containing
 *         the first occurrence of the edge data associating
 *         source and sink vertex data. If there is no edges linking
 *         source and sink vertices, NULL value is returned instead. 
 */
xme_core_pnp_dataLinkGraph_edgeData_t*
xme_core_pnp_lrm_findLogicalRoute
(
    xme_core_pnp_dataLinkGraph_vertexData_t* sourceVertexData,
    xme_core_pnp_dataLinkGraph_vertexData_t* sinkVertexData
);

/**
 * \brief Adds a logical route edge to the input graph.
 *
 * \param[in] graph the incoming graph to add the logical route edge. 
 * \param[out] logicalRouteEdge the newly generated logical route edge. 
 * \param[in] sourceVertexId the vertex identifier of source component port vertex.
 * \param[in] sinkVertexId the vertex identifier of sink component port vertex.
 *
 * \retval XME_STATUS_SUCCESS if logical route edge has been added to the graph. 
 * \retval XME_STATUS_INVALID_PARAMETER if input parameters are not valid.
 * \retval XME_STATUS_OUT_OF_RESOURCES if cannot add the edge to the
 *         graph due to lack of resources. 
 * \retval XME_STATUS_INTERNAL_ERROR if cannot generate a valid logical route edge
 *         to the graph. 
 */
xme_status_t
xme_core_pnp_lrm_addLogicalRouteEdge
(
    xme_hal_graph_graph_t* graph,
    xme_core_pnp_dataLinkGraph_edgeData_t** logicalRouteEdge,
    xme_hal_graph_vertexId_t sourceVertexId,
    xme_hal_graph_vertexId_t sinkVertexId
);

/**
 * \brief Removes a logical route edge from the given graph. 
 *
 * \param graph the graph. 
 * \param edgeId the edge identifier to remove. 
 *
 * \retval XME_STATUS_SUCCESS if the edge has been removed from the graph. 
 * \retval XME_STATUS_INVALID_PARAMETER if input parameters are invalid. 
 * \retval XME_STATUS_INTERNAL_ERROR if cannot remove the given edge from the graph. 
 */
xme_status_t 
xme_core_pnp_lrm_removeLogicalRouteEdge
(
    xme_hal_graph_graph_t* graph, 
    xme_hal_graph_edgeId_t edgeId
);

/**
 * \brief Adds a channel mapping to the input graph.
 *
 * \param[in] graph the incoming graph to add the channel mapping edge. 
 * \param[out] channelMappingEdge the newly generated channel mapping edge. 
 * \param[in] sourceVertexId the vertex identifier of source component port vertex.
 * \param[in] sinkVertexId the vertex identifier of sink component port vertex.
 *
 * \retval XME_STATUS_SUCCESS if the channel mapping edge has been added to the graph. 
 * \retval XME_STATUS_INVALID_PARAMETER if input parameters are not valid.
 * \retval XME_STATUS_OUT_OF_RESOURCES if cannot add the edge to the
 *         graph due to lack of resources. 
 * \retval XME_STATUS_INTERNAL_ERROR if cannot generate a valid logical route edge
 *         to the graph. 
 */
xme_status_t
xme_core_pnp_lrm_addChannelMappingEdge
(
    xme_hal_graph_graph_t* graph,
    xme_core_pnp_dataLinkGraph_edgeData_t** channelMappingEdge,
    xme_hal_graph_vertexId_t sourceVertexId,
    xme_hal_graph_vertexId_t sinkVertexId
);

#if 0
/**
 * \brief Function that implements the comparison of two edges
 *        in a given graph. 
 * \note Comparison affects only at logical routes. 
 *
 * \param edgeData1 the first edge data to compare. 
 * \param edgeData2 the second edge data to compare. 
 *
 * \return an int value stating that the first edge data is
 *         lower, equal or greater than the second edge data. 
 */
int 
xme_core_pnp_lrm_compareLogicalRouteEdge
(
    const xme_core_pnp_dataLinkGraph_edgeData_t* const edgeData1, 
    const xme_core_pnp_dataLinkGraph_edgeData_t* const edgeData2
);
#endif

/**
 * \brief Function that implements the comparison of two vertices
 *        in a given graph. 
 * \note Comparison affects only at component port vertex level. 
 *
 * \param vertexData1 the first vertex data to compare. 
 * \param vertexData2 the second vertex data to compare. 
 *
 * \return an int value stating that the first vertex data is
 *         lower, equal or greater than the second vertex data. 
 */
int 
xme_core_pnp_lrm_compareComponentPortVertex
(
    const xme_core_pnp_dataLinkGraph_vertexData_t* const vertexData1, 
    const xme_core_pnp_dataLinkGraph_vertexData_t* const vertexData2
);

/**
 * \brief Checks if two announcement matches.
 * \details Two announcement matches when source announcement publishes
 *          some topic (with associated attributes) and the sink announcement
 *          is waiting for exactly that matching publication. 
 * \param sourceAnnouncement the source announcement.
 * \param sinkAnnouncement the sink announcement. 
 *
 * \retval true if source announcement matchs to sink announcement. 
 * \retval false if source announcement does not match to sink announcement. 
 */
bool xme_core_pnp_lrm_matchAnnouncement
(
    xme_core_pnp_lrm_announcement_t* sourceAnnouncement, 
    xme_core_pnp_lrm_announcement_t* sinkAnnouncement
);

/**
 * \brief Matches announcement attributes. 
 *
 * \details Attributes are metadata associated to the announcement. 
 *          If all attributes in the publication match the filters in 
 *          subscription for the same key in attribute, the return 
 *          value is true. In other case, the value is false. 
 *
 * \note Attribute list on subscription side can be empty. In this case,
 *       the result is true. 
 *
 * \param sourceAnnouncement The publication announcement, in which is
 *        contained the attribute definitions. 
 * \param sinkAnnouncement The subscription announcement, in which is 
 *        defined the attribute filters. 
 *
 * \retval true if the source announcement attribute definitions match
 *         with the sink announcement attribute filters. 
 * \retval false if there is at least one non-matching attribute
 *         between source announcement attribute definitions and
 *         sink announcement attribute filters.
 */
bool
xme_core_pnp_lrm_matchAttributes
(
    const xme_core_pnp_lrm_announcement_t* const sourceAnnouncement, 
    const xme_core_pnp_lrm_announcement_t* const sinkAnnouncement
);

/**
 * \brief Checks if the current vertex data belongs to a component port. 
 * 
 * \param[in] vertexData the pointer to the vertex data. 
 *
 * \retval true if the vertex data is a component port. 
 * \retval false if the vertex data is NOT a component port. 
 */
bool
xme_core_pnp_lrm_isComponentPort
(
    const xme_core_pnp_dataLinkGraph_vertexData_t* const vertexData
);

/**
 * \brief Checks if the current vertex data is a publication. 
 * \note The assumption is that the provided vertex data is already a component port. 
 * \note A publication can be either a DCC Publication or a Request/Response sender.
 * 
 * \param[in] vertexData the pointer to the vertex data. 
 *
 * \retval true if the vertex data is a publication. 
 * \retval false if the vertex data is NOT a publication. 
 */
bool
xme_core_pnp_lrm_isComponentPortPublication
(
    const xme_core_pnp_dataLinkGraph_vertexData_t* const vertexData
);

/**
 * \brief Checks if the current vertex data is a subscription. 
 * \note The assumption is that the provided vertex data is already a component port. 
 * \note A subscription can be either a DCC Subscription or a Request/Response handler.
 * 
 * \param[in] vertexData the pointer to the vertex data. 
 *
 * \retval true if the vertex data is a subscription. 
 * \retval false if the vertex data is NOT a subscription. 
 */
bool
xme_core_pnp_lrm_isComponentPortSubscription
(
    const xme_core_pnp_dataLinkGraph_vertexData_t* const vertexData
);

//// FIXME: Export? Do we need that?
//int xme_core_pnp_lrm_compare_topic(xme_core_topic_t* topic1, xme_core_topic_t* topic2);
//
//// FIXME: Export?
//int xme_core_pnp_lrm_compare_channelId(xme_core_channelId_t* channelId1, xme_core_channelId_t* channelId2);
//
//// FIXME: Export?
//int xme_core_pnp_lrm_compare_component(xme_core_component_t* component1, xme_core_component_t* component2);
//
//// FIXME: Export?
//int xme_core_pnp_lrm_compare_containerVertexData(xme_core_pnp_lrm_announcement_t* announcement1, xme_core_pnp_lrm_announcement_t* announcement2);
//
//// FIXME: Export?
//int xme_core_pnp_lrm_compare_logicalRouteEdgeData(xme_core_pnp_lrm_announcement_t* announcement1, xme_core_pnp_lrm_announcement_t* announcement2);

/**
 * \brief Creates the channel mappings between requests and responses.
 * \details To implement the request-response pattern, we need to link requests to responses
 *          in the logical route. For this purpose, we should add a link between request handler and
 *          response sender to map the request channel and the response channel. We will do this
 *          by creating a new edge type (named ChannelMapping) to store this mapping information. 
 * \note This function removes the set of orphan requests and orphan responses. 
 *
 * \param[in] routes the set of logical routes established after checking publications and subscriptions. 
 *            After the function, the request-response logical routes are linked using ChannelMapping edge type. 
 *
 * \retval XME_STATUS_SUCCESS if the channel mapping edges are successfully created. 
 * \retval XME_STATUS_INTERNAL_ERROR if there were an error in analysis and creation of channel mappings.
 *                                   this would include eventually errors in the orphan requests/responses removal. 
 */
xme_status_t
xme_core_pnp_lrm_createChannelMappings
(
    xme_core_pnp_lrm_logicalRoutes_t* routes
);

/**
 * \brief Generates a channel identifier to connect two ports. 
 *
 * \param[in] transactionID Transaction identifier this operation belong to.
 *            This value may be set to XME_CORE_TRANSACTION_INVALID_TRANSACTION_ID
 *            in order to indicate that this announcement can not be rolled back.
 * \param[out] outChannelID The generated channel identifier.
 *
 * \retval XME_STATUS_SUCCESS if the channel identifier was successfully
 *         generated.
 * \retval XME_STATUS_INVALID_PARAMETER if the transaction identifier was
 *         invalid or outChannelId was NULL.
 * \retval XME_STATUS_INTERNAL_ERROR if the new channel identifier could not be
 *         generated.
 */
xme_status_t
xme_core_pnp_lrm_generateChannelID
(
    xme_core_transactionId_t transactionID,
    xme_core_channelId_t* outChannelID
);

/******************************************************************************/
/***   Implementation                                                       ***/
/******************************************************************************/

xme_status_t
xme_core_pnp_lrm_init
(
    void *params
)
{
    XME_UNUSED_PARAMETER(params);

    // Forget previous configuration (required for independence of test cases)
    // TODO: Solve in a clean way. If a test intiailizes the LRM, then it should also finalize it!
    //       As such, the correct statement here is: XME_ASSERT(!xme_core_pnp_lrm_state.initialized);
    if (xme_core_pnp_lrm_state.initialized)
    {
        xme_core_pnp_lrm_fini();
    }

    XME_HAL_SINGLYLINKEDLIST_INIT(xme_core_pnp_lrm_state.graphs);
    XME_HAL_TABLE_INIT(publication_subscription_channel_mappings_table);

    // Establish the first value of channel id already assigned. 
    xme_core_pnp_lrm_state.maxChannelID = (xme_core_channelId_t) XME_CORE_PNP_LRM_INITIAL_CHANNELID;

    xme_core_pnp_lrm_state.initialized = true;

    return XME_STATUS_SUCCESS;
}

void
xme_core_pnp_lrm_fini(void)
{
    XME_ASSERT_NORVAL(xme_core_pnp_lrm_state.initialized);

    // Forget previous configuration and free memory (required for independence of test cases)
    XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN(
        xme_core_pnp_lrm_state.graphs, 
        xme_core_pnp_lrm_stateSubGraph_t, 
        stateSubgraph
    );
    {
        (void) xme_core_pnp_lrm_removeSubGraph(&stateSubgraph);
    }
    XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

    xme_hal_singlyLinkedList_fini(&xme_core_pnp_lrm_state.graphs);
    XME_HAL_TABLE_FINI(publication_subscription_channel_mappings_table);

    xme_core_pnp_lrm_state.maxTransactionId = XME_CORE_INVALID_TRANSACTION_ID;
    xme_core_pnp_lrm_state.maxChannelID = XME_CORE_INVALID_CHANNEL_ID;

    xme_core_pnp_lrm_state.initialized = false;
}

/*
 * Maybe, we need an additional parameter attrList for specifying required
 * attributes of a response (only required for request ports)
 */
xme_status_t
xme_core_pnp_lrm_announcePort
(
    xme_core_node_nodeId_t nodeId,
    xme_core_component_t componentId,
    xme_core_componentType_t componentType,
    xme_core_component_portType_t portType,
    uint16_t portIndex,
    xme_core_component_connectionBound_t lowerConnectionBound,
    xme_core_component_connectionBound_t upperConnectionBound,
    xme_core_topic_t topicId,
    xme_core_directory_attributeSetHandle_t attrSet,
    xme_core_transactionId_t transactionId
)
{
    xme_core_pnp_lrm_announcement_t* newAnnouncement;
    xme_core_pnp_lrm_stateSubGraph_t* subGraph;

    XME_CHECK(XME_CORE_NODE_INVALID_NODE_ID != nodeId, XME_STATUS_INVALID_PARAMETER);
    XME_CHECK(XME_CORE_COMPONENT_INVALID_COMPONENT_CONTEXT != componentId, XME_STATUS_INVALID_PARAMETER);
    XME_CHECK(XME_CORE_COMPONENT_PORTTYPE_INVALID != portType, XME_STATUS_INVALID_PARAMETER);
    XME_CHECK(XME_CORE_TOPIC_INVALID_TOPIC != topicId, XME_STATUS_INVALID_PARAMETER);
    
    // We only support the common DCC and RR port types
    XME_CHECK
    (
        XME_CORE_COMPONENT_PORTTYPE_DCC_PUBLICATION == portType ||
        XME_CORE_COMPONENT_PORTTYPE_DCC_SUBSCRIPTION == portType ||
        XME_CORE_COMPONENT_PORTTYPE_RR_REQUEST_SENDER == portType ||
        XME_CORE_COMPONENT_PORTTYPE_RR_REQUEST_HANDLER == portType ||
        XME_CORE_COMPONENT_PORTTYPE_RR_RESPONSE_SENDER == portType ||
        XME_CORE_COMPONENT_PORTTYPE_RR_RESPONSE_HANDLER == portType,
        XME_STATUS_INVALID_PARAMETER
    );

    // Check if lower/upperConnectionBound is valid for given portType
    {
        xme_status_t status = xme_core_pnp_dataLinkGraph_checkComponentPort(portType, lowerConnectionBound, upperConnectionBound);
        XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INVALID_CONFIGURATION);
    }

    subGraph = NULL;

    // Search for the subgraph with the same topicId.
    // TODO: Add attribute support capability as soon as it is scheduled.
    XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN(
        xme_core_pnp_lrm_state.graphs, 
        xme_core_pnp_lrm_stateSubGraph_t, 
        stateSubgraph);
    {
        if (stateSubgraph->topicId == topicId)
        {
            subGraph = stateSubgraph;
            break;
        }
    }
    XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

    // If it does not exists, create a new subgraph and add it to the internal state. 
    if (subGraph == NULL)
    {
        XME_CHECK(xme_core_pnp_lrm_createSubGraph(&subGraph) == XME_STATUS_SUCCESS, XME_STATUS_INTERNAL_ERROR);

        subGraph->topicId = topicId;
        subGraph->transactionId = transactionId;
    }

    XME_ASSERT(subGraph != NULL);


    /* //FIXME:Assumptions: 
     * - All announcements will be kept forever unless they are explicitly rolled back
     * - If an announcement already exists from an earlier transaction, the older version will be used. (Ok?)
     */ 
    newAnnouncement = (xme_core_pnp_lrm_announcement_t*)xme_hal_mem_alloc(sizeof(xme_core_pnp_lrm_announcement_t));
        
    XME_CHECK(newAnnouncement != NULL, XME_STATUS_OUT_OF_RESOURCES);

    newAnnouncement->nodeId = nodeId; 
    newAnnouncement->componentId = componentId;
    newAnnouncement->componentType = componentType;
    newAnnouncement->portType = portType;
    newAnnouncement->topicId = topicId;
    newAnnouncement->portIndex = portIndex;
    newAnnouncement->lowerConnectionBound = lowerConnectionBound;
    newAnnouncement->upperConnectionBound = upperConnectionBound;
    newAnnouncement->attrSet = attrSet;
    newAnnouncement->transactionId = transactionId;

    // Find a similar announcement in publications and subscriptions list. 
    switch(portType)
    {
        // We will use publications for Data-Centric Communication, a Request Sender and a Response Sender (Request-Response pattern). 
        case XME_CORE_COMPONENT_PORTTYPE_DCC_PUBLICATION:
        case XME_CORE_COMPONENT_PORTTYPE_RR_REQUEST_SENDER:
        case XME_CORE_COMPONENT_PORTTYPE_RR_RESPONSE_SENDER:
            XME_CHECK(XME_STATUS_SUCCESS == xme_hal_singlyLinkedList_addItem(&subGraph->publications, newAnnouncement), XME_STATUS_INTERNAL_ERROR);
            break;
        case XME_CORE_COMPONENT_PORTTYPE_DCC_SUBSCRIPTION:
        case XME_CORE_COMPONENT_PORTTYPE_RR_REQUEST_HANDLER:
        case XME_CORE_COMPONENT_PORTTYPE_RR_RESPONSE_HANDLER:
            XME_CHECK(XME_STATUS_SUCCESS == xme_hal_singlyLinkedList_addItem(&subGraph->subscriptions, newAnnouncement), XME_STATUS_INTERNAL_ERROR);
            break;
        case XME_CORE_COMPONENT_PORTTYPE_INVALID:
        case XME_CORE_COMPONENT_PORTTYPE_INTERNAL:
        default:
            XME_LOG
            (
                XME_LOG_NOTE,
                "[LRM] The announcement for node %d, component %d, and topic %d cannot be completed: Port Type %d not supported\n",
                nodeId,
                componentId,
                topicId,
                portType
            );
            return XME_STATUS_UNSUPPORTED;
    }
#if 0
    if (portType == XME_CORE_COMPONENT_PORTTYPE_DCC_PUBLICATION)
    {
        XME_CHECK(XME_STATUS_SUCCESS == xme_hal_singlyLinkedList_addItem(&subGraph->publications, newAnnouncement), XME_STATUS_INTERNAL_ERROR);
    }
    else if (portType == XME_CORE_COMPONENT_PORTTYPE_DCC_SUBSCRIPTION)
    {
        XME_CHECK(XME_STATUS_SUCCESS == xme_hal_singlyLinkedList_addItem(&subGraph->subscriptions, newAnnouncement), XME_STATUS_INTERNAL_ERROR);
    }
#endif

    subGraph->transactionId = transactionId;

    return XME_STATUS_SUCCESS;
}

xme_status_t
xme_core_pnp_lrm_unannouncePortForComponent
(
    xme_core_node_nodeId_t nodeID,
    xme_core_component_t componentID,
    xme_core_transactionId_t transactionId
)
{
    XME_CHECK(XME_CORE_NODE_INVALID_NODE_ID != nodeID, XME_STATUS_INVALID_PARAMETER);
    XME_CHECK(XME_CORE_COMPONENT_INVALID_COMPONENT_CONTEXT != componentID, XME_STATUS_INVALID_PARAMETER);

    XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN(
        xme_core_pnp_lrm_state.graphs, 
        xme_core_pnp_lrm_stateSubGraph_t, 
        stateSubgraph);
    {
        if (stateSubgraph->transactionId == transactionId)
        {
            // Remove announcements from subscriptions. 
            XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN
            (
                stateSubgraph->subscriptions, 
                xme_core_pnp_lrm_announcement_t, 
                subscription
            );
            {
                if (subscription->nodeId == nodeID &&
                    subscription->componentId == componentID)
                {
                    // Found one subscription to remove. 
                    XME_CHECK
                    (
                        0 < xme_hal_singlyLinkedList_removeItem(&stateSubgraph->subscriptions, subscription, (bool) false), 
                        XME_STATUS_INTERNAL_ERROR
                    );
                }
            }
            XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

            // Remove announcements from publications. 
            XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN
            (
                stateSubgraph->publications, 
                xme_core_pnp_lrm_announcement_t, 
                publication
            );
            {
                if (publication->nodeId == nodeID &&
                    publication->componentId == componentID)
                {
                    // Found one subscription to remove. 
                    XME_CHECK
                    (
                        0 < xme_hal_singlyLinkedList_removeItem(&stateSubgraph->publications, publication, (bool) false), 
                        XME_STATUS_INTERNAL_ERROR
                    );
                }
            }
            XME_HAL_SINGLYLINKEDLIST_ITERATE_END();
        }
    }
    XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

    return XME_STATUS_SUCCESS;
}


xme_status_t
xme_core_pnp_lrm_replaceContainerId
(
    xme_core_node_nodeId_t oldNodeId,
    xme_core_container_t oldContainerId,
    xme_core_node_nodeId_t newNodeId,
    xme_core_container_t newContainerId,
    xme_core_transactionId_t transactionId
)
{
    XME_UNUSED_PARAMETER(oldNodeId);
    XME_UNUSED_PARAMETER(oldContainerId);
    XME_UNUSED_PARAMETER(newNodeId);
    XME_UNUSED_PARAMETER(newContainerId);
    XME_UNUSED_PARAMETER(transactionId);

    //TODO: Find all affected channels and announcements and modify them

    return XME_STATUS_SUCCESS;
}

/*
 * This function will not do anything because channelIds will be globally unique
 */
xme_status_t
xme_core_pnp_lrm_discardChannelId
(
    xme_core_channelId_t channelId
)
{
    XME_UNUSED_PARAMETER(channelId);

    return XME_STATUS_SUCCESS;
}

xme_status_t
xme_core_pnp_lrm_generateChannelID
(
    xme_core_transactionId_t transactionID,
    xme_core_channelId_t* outChannelID
)
{
    /*
    The support of both channelId and transactionId is related with commit and rollback functions (currently developed at dataHandler level).

    The channel id will be generated associated to a given logical route. In case that the route is not finally established, the corresponding channelId generation is rolled back. Just for that unique purpose, and for maintaining traceability of operations in Chromosome, we should maintain transactionId as part of the current LRM interface and implementation, even if we do not use it at all.
    */
    XME_UNUSED_PARAMETER(transactionID);

    XME_CHECK(NULL != outChannelID, XME_STATUS_INVALID_PARAMETER);

    xme_core_pnp_lrm_state.maxChannelID = (xme_core_channelId_t)(((xme_maxSystemValue_t)xme_core_pnp_lrm_state.maxChannelID) + 1);

    *outChannelID = xme_core_pnp_lrm_state.maxChannelID;

    //TODO: Define the valididy of a channel ID. (per transaction? globally?)
    //TODO: Check for maximum Value
    return (XME_STATUS_SUCCESS);
}

xme_status_t
xme_core_pnp_lrm_getChannelID
(
    xme_core_transactionId_t transactionID,
    xme_core_node_nodeId_t publicationNodeID,
    xme_core_component_t publicationComponentID,
    uint16_t publicationPortIndex,
    xme_core_node_nodeId_t subscriptionNodeID,
    xme_core_component_t subscriptionComponentID,
    uint16_t subscriptionPortIndex,
    xme_core_channelId_t* outChannelID
)
{
    /*
    The support of both channelId and transactionId is related with commit and rollback
    functions (currently developed at dataHandler level).
    The channel id will be generated associated to a given logical route.
    In case that the route is not finally established, the corresponding channelId
    generation is rolled back.  Just for that unique purpose, and for maintaining
    traceability of operations in Chromosome, we should maintain transactionId as part
    of the current LRM interface and implementation, even if we do not use it at all.
    */
    xme_hal_table_rowHandle_t rh = XME_HAL_TABLE_INVALID_ROW_HANDLE;
    publication_subscription_channel_mappings_t* item = NULL;
    XME_UNUSED_PARAMETER(transactionID);

    XME_CHECK(NULL != outChannelID, XME_STATUS_INVALID_PARAMETER);

    XME_HAL_TABLE_GET_NEXT
    (
        publication_subscription_channel_mappings_table,
        xme_hal_table_rowHandle_t, rh,
        publication_subscription_channel_mappings_t, item,
        (  
            item->publicationNodeID == publicationNodeID &&
            item->publicationComponentID == publicationComponentID &&
            item->publicationPortIndex == publicationPortIndex &&
            item->subscriptionNodeID == subscriptionNodeID &&
            item->subscriptionComponentID == subscriptionComponentID &&
            item->subscriptionPortIndex == subscriptionPortIndex
        )
    );
    
    if (XME_HAL_TABLE_INVALID_ROW_HANDLE == rh)
    {
        return xme_core_pnp_lrm_generateChannelID(transactionID, outChannelID);
        /* We do not add the newly created the channel ID in the table */
    }
    else
    {
        *outChannelID = item->channelID;
        //Optimization, we don't need this channel ID again as it will be the updated in the logical route
        //So we can safely delete it
        //XME_HAL_TABLE_REMOVE_ITEM(publication_subscription_channel_mappings_table, rh);
        return XME_STATUS_SUCCESS;
    }
}

xme_status_t
xme_core_pnp_lrm_announceChannelID
(
    xme_core_node_nodeId_t publicationNodeID,
    xme_core_component_t publicationComponentID,
    uint16_t publicationPortIndex,
    xme_core_node_nodeId_t subscriptionNodeID,
    xme_core_component_t subscriptionComponentID,
    uint16_t subscriptionPortIndex,
    xme_core_channelId_t channelID
)
{
    xme_hal_table_rowHandle_t rh = XME_HAL_TABLE_INVALID_ROW_HANDLE;
    publication_subscription_channel_mappings_t* item = NULL;

    rh = XME_HAL_TABLE_ADD_ITEM(publication_subscription_channel_mappings_table);
    
    XME_CHECK(XME_HAL_TABLE_INVALID_ROW_HANDLE != rh, XME_STATUS_OUT_OF_RESOURCES);

    item = XME_HAL_TABLE_ITEM_FROM_HANDLE(publication_subscription_channel_mappings_table, rh);
    
    item->publicationNodeID = publicationNodeID;
    item->publicationComponentID = publicationComponentID;
    item->publicationPortIndex = publicationPortIndex;
    item->subscriptionNodeID = subscriptionNodeID;
    item->subscriptionComponentID = subscriptionComponentID;
    item->subscriptionPortIndex = subscriptionPortIndex;
    item->channelID = channelID;
            
    return XME_STATUS_SUCCESS;
}

xme_status_t
xme_core_pnp_lrm_getLogicalRoutes
(
    xme_core_transactionId_t transactionId,
    xme_core_pnp_lrm_logicalRoutes_t* logicalRoutes
)
{
    xme_hal_linkedList_descriptor_t publicationVertices;
    xme_hal_linkedList_descriptor_t subscriptionVertices;
    xme_core_pnp_dataLinkGraph_vertexData_t* vertexPtr;
    xme_core_pnp_dataLinkGraph_vertexData_t* vertexToUse;
    xme_core_pnp_dataLinkGraph_edgeData_t* edgePtr;
    xme_core_pnp_dataLinkGraph_edgeData_t* edgeToUse;
    xme_core_channelId_t channelId;
    xme_hal_graph_vertexId_t sourceVertexId;
    xme_hal_graph_vertexId_t sinkVertexId;

    XME_CHECK(NULL != logicalRoutes, XME_STATUS_INVALID_PARAMETER);

    // TODO: check for validity of t'Id: XME_CHECK(transactionId == ...
    XME_CHECK(xme_hal_graph_init((xme_core_pnp_lrm_logicalRoutes_t*)logicalRoutes) == XME_STATUS_SUCCESS, XME_STATUS_INTERNAL_ERROR);

    XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN(xme_core_pnp_lrm_state.graphs, xme_core_pnp_lrm_stateSubGraph_t, subGraph);
    {
        if (subGraph->transactionId == transactionId)
        {
            XME_HAL_SINGLYLINKEDLIST_INIT(publicationVertices);
            XME_HAL_SINGLYLINKEDLIST_INIT(subscriptionVertices);
            
            /*
             * How it works:
             * 
             * 1. Update local route Graph elements
             * 2. Update local route status
             * 3. COPY routes to logicalRoutes
             */

            // 1. Update graph elements

            // 1a. subscriptions
            XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN
            (
                subGraph->subscriptions, 
                xme_core_pnp_lrm_announcement_t, 
                subscription
            );
            {
                xme_status_t status;
                vertexToUse = NULL;

                status = xme_hal_graph_initVertexIterator(&subGraph->routes);
                XME_ASSERT(XME_STATUS_SUCCESS == status);
                while(xme_hal_graph_hasNextVertex(&subGraph->routes))
                {        
                    XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getVertexData(&subGraph->routes, xme_hal_graph_nextVertex(&subGraph->routes), (void**)&vertexPtr), XME_STATUS_INTERNAL_ERROR);

                    if (vertexPtr->vertexType == XME_CORE_PNP_DATALINKGRAPH_VERTEXTYPE_COMPONENT_PORT &&
                        vertexPtr->topicId == subscription->topicId &&
                        vertexPtr->vertexData.componentPortVertex.componentId == subscription->componentId &&
                        vertexPtr->vertexData.componentPortVertex.componentType == subscription->componentType &&
                        vertexPtr->nodeId == subscription->nodeId &&
                        vertexPtr->vertexData.componentPortVertex.portIndex == subscription->portIndex &&
                        vertexPtr->vertexData.componentPortVertex.lowerConnectionBound == subscription->lowerConnectionBound &&
                        vertexPtr->vertexData.componentPortVertex.upperConnectionBound == subscription->upperConnectionBound &&
                        vertexPtr->vertexData.componentPortVertex.portType == subscription->portType)
                    {
                        vertexToUse = vertexPtr;
                    }
                }
                status = xme_hal_graph_finiVertexIterator(&subGraph->routes);
                XME_ASSERT(XME_STATUS_SUCCESS == status);

                if (vertexToUse == NULL) {
                    XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_addComponentPortVertex(&subGraph->routes, &vertexToUse), XME_STATUS_INTERNAL_ERROR);

                    vertexToUse->nodeId = subscription->nodeId;
                    vertexToUse->topicId = subscription->topicId;
                    vertexToUse->vertexData.componentPortVertex.portIndex = subscription->portIndex;
                    vertexToUse->vertexData.componentPortVertex.lowerConnectionBound = subscription->lowerConnectionBound;
                    vertexToUse->vertexData.componentPortVertex.upperConnectionBound = subscription->upperConnectionBound;
                    vertexToUse->vertexData.componentPortVertex.componentId = subscription->componentId;
                    vertexToUse->vertexData.componentPortVertex.componentType = subscription->componentType;
                    vertexToUse->vertexData.componentPortVertex.portType = subscription->portType;
                    vertexToUse->vertexData.componentPortVertex.announcement = subscription;
                }

                XME_CHECK(XME_STATUS_SUCCESS == xme_hal_singlyLinkedList_addItem(&subscriptionVertices, vertexToUse), XME_STATUS_INTERNAL_ERROR);

                // 3. clone and add to logicalRoutes
                vertexPtr = NULL;
                XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_addComponentPortVertex(logicalRoutes, &vertexPtr), XME_STATUS_INTERNAL_ERROR);

                XME_CHECK(vertexPtr != NULL, XME_STATUS_OUT_OF_RESOURCES);

                vertexPtr->nodeId = vertexToUse->nodeId;
                vertexPtr->topicId = vertexToUse->topicId;
                vertexPtr->vertexData.componentPortVertex.componentId = vertexToUse->vertexData.componentPortVertex.componentId;
                vertexPtr->vertexData.componentPortVertex.componentType = vertexToUse->vertexData.componentPortVertex.componentType;
                vertexPtr->vertexData.componentPortVertex.portType = vertexToUse->vertexData.componentPortVertex.portType;
                vertexPtr->vertexData.componentPortVertex.portIndex = vertexToUse->vertexData.componentPortVertex.portIndex;
                vertexPtr->vertexData.componentPortVertex.lowerConnectionBound = vertexToUse->vertexData.componentPortVertex.lowerConnectionBound;
                vertexPtr->vertexData.componentPortVertex.upperConnectionBound = vertexToUse->vertexData.componentPortVertex.upperConnectionBound;
                vertexPtr->vertexData.componentPortVertex.announcement = vertexToUse->vertexData.componentPortVertex.announcement;

            }
            XME_HAL_SINGLYLINKEDLIST_ITERATE_END();


            // 1b. publications
            XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN(subGraph->publications, xme_core_pnp_lrm_announcement_t, publication);
            {
                xme_status_t status;
                vertexToUse = NULL;

                status = xme_hal_graph_initVertexIterator(&subGraph->routes);
                XME_ASSERT(XME_STATUS_SUCCESS == status);
                while(xme_hal_graph_hasNextVertex(&subGraph->routes))
                {        
                    XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getVertexData(&subGraph->routes, xme_hal_graph_nextVertex(&subGraph->routes), (void**)&vertexPtr), XME_STATUS_INTERNAL_ERROR);

                    if (vertexPtr->vertexType == XME_CORE_PNP_DATALINKGRAPH_VERTEXTYPE_COMPONENT_PORT &&
                        vertexPtr->topicId == publication->topicId &&
                        vertexPtr->vertexData.componentPortVertex.componentId == publication->componentId &&
                        vertexPtr->vertexData.componentPortVertex.componentType == publication->componentType &&
                        vertexPtr->nodeId == publication->nodeId &&
                        vertexPtr->vertexData.componentPortVertex.portIndex == publication->portIndex &&
                        vertexPtr->vertexData.componentPortVertex.lowerConnectionBound == publication->lowerConnectionBound &&
                        vertexPtr->vertexData.componentPortVertex.upperConnectionBound == publication->upperConnectionBound &&
                        vertexPtr->vertexData.componentPortVertex.portType == publication->portType)
                    {
                        vertexToUse = vertexPtr;
                    }
                }
                status = xme_hal_graph_finiVertexIterator(&subGraph->routes);
                XME_ASSERT(XME_STATUS_SUCCESS == status);

                if (vertexToUse == NULL) {
                    XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_addComponentPortVertex(&subGraph->routes, &vertexToUse), XME_STATUS_INTERNAL_ERROR);

                    vertexToUse->nodeId = publication->nodeId;
                    vertexToUse->topicId = publication->topicId;
                    vertexToUse->vertexData.componentPortVertex.portIndex = publication->portIndex;
                    vertexToUse->vertexData.componentPortVertex.lowerConnectionBound = publication->lowerConnectionBound;
                    vertexToUse->vertexData.componentPortVertex.upperConnectionBound = publication->upperConnectionBound;
                    vertexToUse->vertexData.componentPortVertex.componentId = publication->componentId;
                    vertexToUse->vertexData.componentPortVertex.componentType = publication->componentType;
                    vertexToUse->vertexData.componentPortVertex.portType = publication->portType;
                    vertexToUse->vertexData.componentPortVertex.announcement = publication;
                }

                XME_CHECK(XME_STATUS_SUCCESS == xme_hal_singlyLinkedList_addItem(&publicationVertices, vertexToUse), XME_STATUS_INTERNAL_ERROR);

                // 3. clone and add to logicalRoutes
                vertexPtr = NULL;
                XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_addComponentPortVertex(logicalRoutes, &vertexPtr), XME_STATUS_INTERNAL_ERROR);

                XME_CHECK(vertexPtr != NULL, XME_STATUS_OUT_OF_RESOURCES);

                vertexPtr->nodeId = vertexToUse->nodeId;
                vertexPtr->topicId = vertexToUse->topicId;
                vertexPtr->vertexData.componentPortVertex.componentId = vertexToUse->vertexData.componentPortVertex.componentId;
                vertexPtr->vertexData.componentPortVertex.componentType = vertexToUse->vertexData.componentPortVertex.componentType;
                vertexPtr->vertexData.componentPortVertex.portType = vertexToUse->vertexData.componentPortVertex.portType;
                vertexPtr->vertexData.componentPortVertex.portIndex = vertexToUse->vertexData.componentPortVertex.portIndex;
                vertexPtr->vertexData.componentPortVertex.lowerConnectionBound = vertexToUse->vertexData.componentPortVertex.lowerConnectionBound;
                vertexPtr->vertexData.componentPortVertex.upperConnectionBound = vertexToUse->vertexData.componentPortVertex.upperConnectionBound;
                vertexPtr->vertexData.componentPortVertex.announcement = vertexToUse->vertexData.componentPortVertex.announcement;
            }
            XME_HAL_SINGLYLINKEDLIST_ITERATE_END();


            // 2. Update edges
            XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN(
                subscriptionVertices, 
                xme_core_pnp_dataLinkGraph_vertexData_t, 
                subscriptionVertex
            );
            {
                XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN(
                    publicationVertices, 
                    xme_core_pnp_dataLinkGraph_vertexData_t, 
                    publicationVertex
                );
                {
                    if (xme_core_pnp_lrm_matchAnnouncement(
                        publicationVertex->vertexData.componentPortVertex.announcement, 
                        subscriptionVertex->vertexData.componentPortVertex.announcement))
                    {
                        xme_status_t status;

                        /*
                         * check if route already exists in local route Graph. 
                         * steps:
                         * 1. reverse lookup all incoming edges
                         * 2. search each edge for source that matches publication
                         * 3. add missing edges
                         */
                        edgeToUse = NULL;

                        status = xme_hal_graph_initIncomingEdgeIterator(&subGraph->routes, subscriptionVertex->vertexId);
                        XME_ASSERT(XME_STATUS_SUCCESS == status);
                        while (xme_hal_graph_hasNextIncomingEdge(&subGraph->routes, subscriptionVertex->vertexId))
                        {
                            xme_hal_graph_edgeId_t edgeId;
                            xme_hal_graph_vertexId_t vertexId;

                            /*xme_hal_graph_getEdgeData(&subGraph->routes, xme_hal_graph_nextIncomingEdge(&subGraph->routes, subscriptionVertex->vertexId), &edgePtr);*/

                            edgeId = xme_hal_graph_nextIncomingEdge(&subGraph->routes, subscriptionVertex->vertexId);

                            XME_CHECK(XME_HAL_GRAPH_INVALID_EDGE_ID != edgeId, XME_STATUS_INTERNAL_ERROR);

                            vertexId = xme_hal_graph_getSourceVertex(&subGraph->routes, edgeId);

                            XME_CHECK(XME_HAL_GRAPH_INVALID_VERTEX_ID != vertexId, XME_STATUS_INTERNAL_ERROR);

                            if (publicationVertex->vertexId == vertexId)
                            {
                                XME_CHECK(xme_hal_graph_getEdgeData(&subGraph->routes, edgeId, (void**) &edgeToUse) == XME_STATUS_SUCCESS, XME_STATUS_INTERNAL_ERROR);
                            }
                        }
                        status = xme_hal_graph_finiIncomingEdgeIterator(&subGraph->routes, subscriptionVertex->vertexId);
                        XME_ASSERT(XME_STATUS_SUCCESS == status);

                        if (edgeToUse == NULL)
                        {
                            XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_addLogicalRouteEdge(&subGraph->routes, &edgeToUse, publicationVertex->vertexId, subscriptionVertex->vertexId), XME_STATUS_INTERNAL_ERROR);

                            XME_CHECK(NULL != edgeToUse, XME_STATUS_OUT_OF_RESOURCES);

                            status = xme_core_pnp_lrm_getChannelID
                            (
                                publicationVertex->vertexData.componentPortVertex.announcement->transactionId,
                                publicationVertex->nodeId,
                                publicationVertex->vertexData.componentPortVertex.componentId,
                                publicationVertex->vertexData.componentPortVertex.portIndex,
                                subscriptionVertex->nodeId,
                                subscriptionVertex->vertexData.componentPortVertex.componentId,
                                subscriptionVertex->vertexData.componentPortVertex.portIndex,
                                &channelId
                            );
                            XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);

                            edgeToUse->edgeData.logicalRouteEdge.topicId = publicationVertex->topicId;
                            edgeToUse->edgeData.logicalRouteEdge.channelId = channelId;
                        }


                        // 3. clone and add to logicalRoutes

                        // find source vertex
                        sourceVertexId = XME_HAL_GRAPH_INVALID_VERTEX_ID;
                        sinkVertexId = XME_HAL_GRAPH_INVALID_VERTEX_ID;
                        vertexPtr = NULL;

                        status = xme_hal_graph_initVertexIterator(logicalRoutes);
                        XME_ASSERT(XME_STATUS_SUCCESS == status);
                        while (xme_hal_graph_hasNextVertex(logicalRoutes))
                        {
                            xme_hal_graph_vertexId_t vertexId;

                            vertexId = xme_hal_graph_nextVertex(logicalRoutes);

                            XME_CHECK(vertexId != XME_HAL_GRAPH_INVALID_VERTEX_ID, XME_STATUS_INTERNAL_ERROR);

                            XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getVertexData(logicalRoutes, vertexId, (void**)&vertexPtr), XME_STATUS_INTERNAL_ERROR);

                            if (xme_core_pnp_lrm_isComponentPort(vertexPtr))
                            {
                                if (xme_core_pnp_lrm_isComponentPortPublication(vertexPtr))
                                {
                                    if (xme_core_pnp_lrm_compareComponentPortVertex(publicationVertex, vertexPtr) == 0)
                                    {
                                        sourceVertexId = vertexId;
                                    }
                                } 
                                else if (xme_core_pnp_lrm_isComponentPortSubscription(vertexPtr))
                                {
                                    if (xme_core_pnp_lrm_compareComponentPortVertex(subscriptionVertex, vertexPtr) == 0)
                                    {
                                        sinkVertexId = vertexId;
                                    }
                                }
                            }
                        }
                        status = xme_hal_graph_finiVertexIterator(logicalRoutes);
                        XME_ASSERT(XME_STATUS_SUCCESS == status);

                        XME_CHECK(sinkVertexId != XME_HAL_GRAPH_INVALID_VERTEX_ID && sourceVertexId != XME_HAL_GRAPH_INVALID_VERTEX_ID, XME_STATUS_INTERNAL_ERROR);

                        {
                            xme_core_pnp_dataLinkGraph_vertexData_t* sourceVertexData = NULL;
                            xme_core_pnp_dataLinkGraph_vertexData_t* sinkVertexData = NULL;

                            status = xme_hal_graph_getVertexData(logicalRoutes, sourceVertexId, (void**) &sourceVertexData);
                            XME_ASSERT(XME_STATUS_SUCCESS == status);

                            status = xme_hal_graph_getVertexData(logicalRoutes, sinkVertexId, (void**) &sinkVertexData);
                            XME_ASSERT(XME_STATUS_SUCCESS == status);

                            edgePtr = xme_core_pnp_lrm_findLogicalRoute(sourceVertexData, sinkVertexData);

                            XME_ASSERT(NULL != edgePtr);
                            
                            XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_addLogicalRouteEdge(logicalRoutes, &edgePtr, sourceVertexId, sinkVertexId), XME_STATUS_INTERNAL_ERROR);

                            XME_CHECK(NULL != edgePtr, XME_STATUS_OUT_OF_RESOURCES);

                            edgePtr->edgeData.logicalRouteEdge.topicId = edgeToUse->edgeData.logicalRouteEdge.topicId;
                            edgePtr->edgeData.logicalRouteEdge.channelId = edgeToUse->edgeData.logicalRouteEdge.channelId;
                        }
                    }
                }
                XME_HAL_SINGLYLINKEDLIST_ITERATE_END();
            }
            XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

            // clean up
            xme_hal_singlyLinkedList_fini(&publicationVertices); // FIXME: just clearing the list instead might be more efficient
            xme_hal_singlyLinkedList_fini(&subscriptionVertices);
        }
    }
    XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

    // Just after all logical routes are calculated, we should create the channel mapping and discard the orphans requests and responses
    XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_createChannelMappings(logicalRoutes), XME_STATUS_INTERNAL_ERROR);

    return XME_STATUS_SUCCESS;
}

xme_status_t
xme_core_pnp_lrm_getEstablishedLogicalRoutesForComponent
(
    xme_core_transactionId_t transactionId,
    xme_core_node_nodeId_t nodeID,
    xme_core_component_t componentID,
    xme_core_pnp_lrm_logicalRoutes_t* logicalRoutes
)
{
    // Initial checks. 
    XME_CHECK(XME_CORE_NODE_INVALID_NODE_ID != nodeID, XME_STATUS_INVALID_PARAMETER);
    XME_CHECK(XME_CORE_COMPONENT_INVALID_COMPONENT_CONTEXT != componentID, XME_STATUS_INVALID_PARAMETER);
    XME_CHECK(NULL != logicalRoutes, XME_STATUS_INVALID_PARAMETER);

    // TODO: check for validity of t'Id: XME_CHECK(transactionId == ...
    XME_CHECK(xme_hal_graph_init((xme_core_pnp_lrm_logicalRoutes_t*)logicalRoutes) == XME_STATUS_SUCCESS, XME_STATUS_INTERNAL_ERROR);

    // Explore all established routes. 
    XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN
    (
        xme_core_pnp_lrm_state.graphs, 
        xme_core_pnp_lrm_stateSubGraph_t, 
        subGraph
    );
    {

        if (subGraph->transactionId == transactionId)
        {

            xme_status_t status;

            status = xme_hal_graph_initVertexIterator(&subGraph->routes);
            XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);

            while(xme_hal_graph_hasNextVertex(&subGraph->routes))
            {
                xme_core_pnp_dataLinkGraph_vertexData_t* vertexPtr;
                xme_core_pnp_dataLinkGraph_edgeData_t* edgeData;

                XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getVertexData(&subGraph->routes, xme_hal_graph_nextVertex(&subGraph->routes), (void**)&vertexPtr), XME_STATUS_INTERNAL_ERROR);

                if (vertexPtr->vertexType == XME_CORE_PNP_DATALINKGRAPH_VERTEXTYPE_COMPONENT_PORT &&
                    vertexPtr->nodeId == nodeID &&
                    vertexPtr->vertexData.componentPortVertex.componentId == componentID)
                {
                    // We have already found one source or sink vertex that matches with the criteria. 

                    // 1. Explore outgoing edges. 
                    status = xme_hal_graph_initOutgoingEdgeIterator(&subGraph->routes, vertexPtr->vertexId);
                    XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);
                    while (xme_hal_graph_hasNextOutgoingEdge(&subGraph->routes, vertexPtr->vertexId))
                    {
                        xme_hal_graph_edgeId_t edgeID;

                        edgeID = xme_hal_graph_nextOutgoingEdge(&subGraph->routes, vertexPtr->vertexId);

                        XME_CHECK(XME_HAL_GRAPH_INVALID_EDGE_ID != edgeID, XME_STATUS_INTERNAL_ERROR);

                        XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getEdgeData(&subGraph->routes, edgeID, (void**) &edgeData), XME_STATUS_INTERNAL_ERROR);

                        if (edgeData->edgeData.logicalRouteEdge.established)
                        {
                            // Output this route to the output variable. 
                            xme_core_pnp_dataLinkGraph_edgeData_t* newEdgeData = NULL;
                            xme_hal_graph_vertexId_t subscriptionVertexId;
                            xme_core_pnp_dataLinkGraph_vertexData_t* sourceVertexData = NULL;
                            xme_core_pnp_dataLinkGraph_vertexData_t* sinkVertexData = NULL;
                            xme_core_pnp_dataLinkGraph_vertexData_t* sourceVertexPtr = NULL;
                            xme_core_pnp_dataLinkGraph_vertexData_t* sinkVertexPtr = NULL;

                            subscriptionVertexId = xme_hal_graph_getSinkVertex(&subGraph->routes, edgeID);

                            status = xme_hal_graph_getVertexData(&subGraph->routes, vertexPtr->vertexId, (void**) &sourceVertexData);
                            XME_ASSERT(XME_STATUS_SUCCESS == status);

                            status = xme_hal_graph_getVertexData(&subGraph->routes, subscriptionVertexId, (void**) &sinkVertexData);
                            XME_ASSERT(XME_STATUS_SUCCESS == status);

                            // Add component port vertex for both publisher and subscriber. 
                            XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_addComponentPortVertex(logicalRoutes, &sourceVertexPtr), XME_STATUS_INTERNAL_ERROR);

                            XME_CHECK(sourceVertexPtr != NULL, XME_STATUS_OUT_OF_RESOURCES);

                            sourceVertexPtr->nodeId = sourceVertexData->nodeId;
                            sourceVertexPtr->topicId = sourceVertexData->topicId;
                            sourceVertexPtr->vertexData.componentPortVertex.componentId = sourceVertexData->vertexData.componentPortVertex.componentId;
                            sourceVertexPtr->vertexData.componentPortVertex.componentType = sourceVertexData->vertexData.componentPortVertex.componentType;
                            sourceVertexPtr->vertexData.componentPortVertex.portType = sourceVertexData->vertexData.componentPortVertex.portType;
                            sourceVertexPtr->vertexData.componentPortVertex.portIndex = sourceVertexData->vertexData.componentPortVertex.portIndex;
                            sourceVertexPtr->vertexData.componentPortVertex.lowerConnectionBound = sourceVertexData->vertexData.componentPortVertex.lowerConnectionBound;
                            sourceVertexPtr->vertexData.componentPortVertex.upperConnectionBound = sourceVertexData->vertexData.componentPortVertex.upperConnectionBound;
                            sourceVertexPtr->vertexData.componentPortVertex.announcement = sourceVertexData->vertexData.componentPortVertex.announcement;

                            XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_addComponentPortVertex(logicalRoutes, &sinkVertexPtr), XME_STATUS_INTERNAL_ERROR);

                            XME_CHECK(sinkVertexPtr != NULL, XME_STATUS_OUT_OF_RESOURCES);

                            sinkVertexPtr->nodeId = sinkVertexData->nodeId;
                            sinkVertexPtr->topicId = sinkVertexData->topicId;
                            sinkVertexPtr->vertexData.componentPortVertex.componentId = sinkVertexData->vertexData.componentPortVertex.componentId;
                            sinkVertexPtr->vertexData.componentPortVertex.componentType = sinkVertexData->vertexData.componentPortVertex.componentType;
                            sinkVertexPtr->vertexData.componentPortVertex.portType = sinkVertexData->vertexData.componentPortVertex.portType;
                            sinkVertexPtr->vertexData.componentPortVertex.portIndex = sinkVertexData->vertexData.componentPortVertex.portIndex;
                            sinkVertexPtr->vertexData.componentPortVertex.lowerConnectionBound = sinkVertexData->vertexData.componentPortVertex.lowerConnectionBound;
                            sinkVertexPtr->vertexData.componentPortVertex.upperConnectionBound = sinkVertexData->vertexData.componentPortVertex.upperConnectionBound;
                            sinkVertexPtr->vertexData.componentPortVertex.announcement = sinkVertexData->vertexData.componentPortVertex.announcement;

                            XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_addLogicalRouteEdge(logicalRoutes, &newEdgeData, sourceVertexPtr->vertexId, sinkVertexPtr->vertexId), XME_STATUS_INTERNAL_ERROR);
                        
                            newEdgeData->edgeData.logicalRouteEdge.topicId = edgeData->edgeData.logicalRouteEdge.topicId;
                            newEdgeData->edgeData.logicalRouteEdge.channelId = edgeData->edgeData.logicalRouteEdge.channelId;
                            newEdgeData->edgeData.logicalRouteEdge.established = false;
                        }
                    }
                    status = xme_hal_graph_finiOutgoingEdgeIterator(&subGraph->routes, vertexPtr->vertexId);

                    // 2. Explore incoming edges. 
                    status = xme_hal_graph_initIncomingEdgeIterator(&subGraph->routes, vertexPtr->vertexId);
                    XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);
                    while (xme_hal_graph_hasNextIncomingEdge(&subGraph->routes, vertexPtr->vertexId))
                    {
                        xme_hal_graph_edgeId_t edgeID;

                        edgeID = xme_hal_graph_nextIncomingEdge(&subGraph->routes, vertexPtr->vertexId);

                        XME_CHECK(XME_HAL_GRAPH_INVALID_EDGE_ID != edgeID, XME_STATUS_INTERNAL_ERROR);

                        XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getEdgeData(&subGraph->routes, edgeID, (void**) &edgeData), XME_STATUS_INTERNAL_ERROR);

                            // Output this route to the output variable. 
                        if (edgeData->edgeData.logicalRouteEdge.established)
                        {
                            // Output this route to the output variable. 
                            xme_core_pnp_dataLinkGraph_edgeData_t* newEdgeData = NULL;
                            xme_hal_graph_vertexId_t publicationVertexId;
                            xme_core_pnp_dataLinkGraph_vertexData_t* sourceVertexData = NULL;
                            xme_core_pnp_dataLinkGraph_vertexData_t* sinkVertexData = NULL;
                            xme_core_pnp_dataLinkGraph_vertexData_t* sourceVertexPtr = NULL;
                            xme_core_pnp_dataLinkGraph_vertexData_t* sinkVertexPtr = NULL;

                            publicationVertexId = xme_hal_graph_getSourceVertex(&subGraph->routes, edgeID);

                            status = xme_hal_graph_getVertexData(&subGraph->routes, publicationVertexId, (void**) &sourceVertexData);
                            XME_ASSERT(XME_STATUS_SUCCESS == status);

                            status = xme_hal_graph_getVertexData(&subGraph->routes, vertexPtr->vertexId, (void**) &sinkVertexData);
                            XME_ASSERT(XME_STATUS_SUCCESS == status);

                            // Add component port vertex for both publisher and subscriber. 
                            XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_addComponentPortVertex(logicalRoutes, &sourceVertexPtr), XME_STATUS_INTERNAL_ERROR);

                            XME_CHECK(sourceVertexPtr != NULL, XME_STATUS_OUT_OF_RESOURCES);

                            sourceVertexPtr->nodeId = sourceVertexData->nodeId;
                            sourceVertexPtr->topicId = sourceVertexData->topicId;
                            sourceVertexPtr->vertexData.componentPortVertex.componentId = sourceVertexData->vertexData.componentPortVertex.componentId;
                            sourceVertexPtr->vertexData.componentPortVertex.componentType = sourceVertexData->vertexData.componentPortVertex.componentType;
                            sourceVertexPtr->vertexData.componentPortVertex.portType = sourceVertexData->vertexData.componentPortVertex.portType;
                            sourceVertexPtr->vertexData.componentPortVertex.portIndex = sourceVertexData->vertexData.componentPortVertex.portIndex;
                            sourceVertexPtr->vertexData.componentPortVertex.lowerConnectionBound = sourceVertexData->vertexData.componentPortVertex.lowerConnectionBound;
                            sourceVertexPtr->vertexData.componentPortVertex.upperConnectionBound = sourceVertexData->vertexData.componentPortVertex.upperConnectionBound;
                            sourceVertexPtr->vertexData.componentPortVertex.announcement = sourceVertexData->vertexData.componentPortVertex.announcement;

                            XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_addComponentPortVertex(logicalRoutes, &sinkVertexPtr), XME_STATUS_INTERNAL_ERROR);

                            XME_CHECK(sinkVertexPtr != NULL, XME_STATUS_OUT_OF_RESOURCES);

                            sinkVertexPtr->nodeId = sinkVertexData->nodeId;
                            sinkVertexPtr->topicId = sinkVertexData->topicId;
                            sinkVertexPtr->vertexData.componentPortVertex.componentId = sinkVertexData->vertexData.componentPortVertex.componentId;
                            sinkVertexPtr->vertexData.componentPortVertex.componentType = sinkVertexData->vertexData.componentPortVertex.componentType;
                            sinkVertexPtr->vertexData.componentPortVertex.portType = sinkVertexData->vertexData.componentPortVertex.portType;
                            sinkVertexPtr->vertexData.componentPortVertex.portIndex = sinkVertexData->vertexData.componentPortVertex.portIndex;
                            sinkVertexPtr->vertexData.componentPortVertex.lowerConnectionBound = sinkVertexData->vertexData.componentPortVertex.lowerConnectionBound;
                            sinkVertexPtr->vertexData.componentPortVertex.upperConnectionBound = sinkVertexData->vertexData.componentPortVertex.upperConnectionBound;
                            sinkVertexPtr->vertexData.componentPortVertex.announcement = sinkVertexData->vertexData.componentPortVertex.announcement;

                            XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_addLogicalRouteEdge(logicalRoutes, &newEdgeData, sourceVertexPtr->vertexId, sinkVertexPtr->vertexId), XME_STATUS_INTERNAL_ERROR);
                        
                            newEdgeData->edgeData.logicalRouteEdge.topicId = edgeData->edgeData.logicalRouteEdge.topicId;
                            newEdgeData->edgeData.logicalRouteEdge.channelId = edgeData->edgeData.logicalRouteEdge.channelId;
                            newEdgeData->edgeData.logicalRouteEdge.established = false;
                        }
                    }
                    status = xme_hal_graph_finiIncomingEdgeIterator(&subGraph->routes, vertexPtr->vertexId);
                }
            }
            status = xme_hal_graph_finiVertexIterator(&subGraph->routes);
        }
    }
    XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

    return XME_STATUS_SUCCESS;
}

xme_status_t
xme_core_pnp_lrm_setLogicalRoute
(
    xme_hal_graph_edgeId_t edge,
    xme_core_channelId_t channelId,
    xme_core_transactionId_t transactionId
)
{
    xme_hal_graph_vertexId_t vertexId;
    xme_hal_graph_edgeId_t edgeId;
    xme_core_pnp_dataLinkGraph_edgeData_t* edgeData;

    XME_UNUSED_PARAMETER(edge);

    XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN(xme_core_pnp_lrm_state.graphs, xme_core_pnp_lrm_stateSubGraph_t, subGraph);
    {
        if (subGraph->transactionId == transactionId)
        {
            xme_status_t status = xme_hal_graph_initVertexIterator(&subGraph->routes);
            XME_ASSERT(XME_STATUS_SUCCESS == status);
            {
                while (xme_hal_graph_hasNextVertex(&subGraph->routes))
                {
                    vertexId = xme_hal_graph_nextVertex(&subGraph->routes);

                    status = xme_hal_graph_initOutgoingEdgeIterator(&subGraph->routes, vertexId);
                    XME_ASSERT(XME_STATUS_SUCCESS == status);
                    {
                        while (xme_hal_graph_hasNextOutgoingEdge(&subGraph->routes, vertexId))
                        {
                            edgeId = xme_hal_graph_nextOutgoingEdge(&subGraph->routes, vertexId);

                            XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getEdgeData(&subGraph->routes, edgeId, (void**) &edgeData), XME_STATUS_INTERNAL_ERROR);

                            if (edgeData->edgeType == XME_CORE_PNP_DATALINKGRAPH_EDGETYPE_LOGICALROUTE &&
                                edgeData->edgeId == edge &&
                                edgeData->edgeData.logicalRouteEdge.channelId == channelId)
                            {
                                xme_status_t result = XME_STATUS_SUCCESS;

                                // Committing the same edge twice is an error
                                if (edgeData->edgeData.logicalRouteEdge.established)
                                {
                                    result = XME_STATUS_INTERNAL_ERROR;
                                }
                                else
                                {
                                    edgeData->edgeData.logicalRouteEdge.established = true;
                                }

                                status = xme_hal_graph_finiOutgoingEdgeIterator(&subGraph->routes, vertexId);
                                XME_ASSERT(XME_STATUS_SUCCESS == status);

                                status = xme_hal_graph_finiVertexIterator(&subGraph->routes);
                                XME_ASSERT(XME_STATUS_SUCCESS == status);

                                return result;
                            }
                        }
                    }
                    status = xme_hal_graph_finiOutgoingEdgeIterator(&subGraph->routes, vertexId);
                    XME_ASSERT(XME_STATUS_SUCCESS == status);
                }
            }
            status = xme_hal_graph_finiVertexIterator(&subGraph->routes);
            XME_ASSERT(XME_STATUS_SUCCESS == status);
        }
    }
    XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

    return XME_STATUS_INVALID_PARAMETER;
}

xme_status_t
xme_core_pnp_lrm_removeLogicalRoute
(
    xme_core_channelId_t channelId,
    xme_core_transactionId_t transactionId
)
{
    xme_hal_graph_vertexId_t vertexId;
    xme_hal_graph_edgeId_t edgeId;
    xme_core_pnp_dataLinkGraph_edgeData_t* edgeData;
	bool removed = false;

    XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN
    (
        xme_core_pnp_lrm_state.graphs, 
        xme_core_pnp_lrm_stateSubGraph_t, 
        subGraph
    );
    {
        if (subGraph->transactionId == transactionId)
        {
            xme_status_t status = xme_hal_graph_initVertexIterator(&subGraph->routes);
            XME_ASSERT(XME_STATUS_SUCCESS == status);
            {
                while (xme_hal_graph_hasNextVertex(&subGraph->routes))
                {
                    vertexId = xme_hal_graph_nextVertex(&subGraph->routes);

                    status = xme_hal_graph_initOutgoingEdgeIterator(&subGraph->routes, vertexId);
                    XME_ASSERT(XME_STATUS_SUCCESS == status);
                    {
                        while (xme_hal_graph_hasNextOutgoingEdge(&subGraph->routes, vertexId))
                        {
                            edgeId = xme_hal_graph_nextOutgoingEdge(&subGraph->routes, vertexId);

                            XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getEdgeData(&subGraph->routes, edgeId, (void**) &edgeData), XME_STATUS_INTERNAL_ERROR);

                            if (edgeData->edgeType == XME_CORE_PNP_DATALINKGRAPH_EDGETYPE_LOGICALROUTE &&
                                edgeData->edgeData.logicalRouteEdge.channelId == channelId)
                            {
                                status = xme_hal_graph_removeEdge(&subGraph->routes, edgeId);
                                XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);
								removed = true;
                            }
                        }
                    }
                    status = xme_hal_graph_finiOutgoingEdgeIterator(&subGraph->routes, vertexId);
                    XME_ASSERT(XME_STATUS_SUCCESS == status);
                }
            }
            status = xme_hal_graph_finiVertexIterator(&subGraph->routes);
            XME_ASSERT(XME_STATUS_SUCCESS == status);
        }
    }
    XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

	if (!removed)
	{
		return XME_STATUS_NOT_FOUND;
	}

    return XME_STATUS_SUCCESS;
}



/////////////////////////////////////////////////////////////////////////////

xme_status_t 
xme_core_pnp_lrm_createSubGraph
(
    xme_core_pnp_lrm_stateSubGraph_t** subGraph
)
{
    XME_CHECK(subGraph != NULL, XME_STATUS_INVALID_PARAMETER);

    *subGraph = (xme_core_pnp_lrm_stateSubGraph_t*)xme_hal_mem_alloc(sizeof(xme_core_pnp_lrm_stateSubGraph_t));

    XME_CHECK(*subGraph != NULL, XME_STATUS_OUT_OF_RESOURCES);

    XME_CHECK(XME_STATUS_SUCCESS == xme_hal_singlyLinkedList_addItem(&xme_core_pnp_lrm_state.graphs, *subGraph), XME_STATUS_INTERNAL_ERROR);

    XME_HAL_SINGLYLINKEDLIST_INIT((*subGraph)->publications);
    XME_HAL_SINGLYLINKEDLIST_INIT((*subGraph)->subscriptions);
    XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_init(&(*subGraph)->routes), XME_STATUS_INTERNAL_ERROR);

    return XME_STATUS_SUCCESS;
}

xme_status_t 
xme_core_pnp_lrm_removeSubGraph
(
    xme_core_pnp_lrm_stateSubGraph_t** subGraph
)
{
    XME_CHECK(subGraph != NULL, XME_STATUS_INVALID_PARAMETER);

    XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN((*subGraph)->publications, xme_core_pnp_lrm_announcement_t, elem);
    {
        xme_hal_mem_free(elem);
    }
    XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

    XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN((*subGraph)->subscriptions, xme_core_pnp_lrm_announcement_t, elem);
    {
        xme_hal_mem_free(elem);
    }
    XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

    XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_clear(&(*subGraph)->routes), XME_STATUS_INTERNAL_ERROR);

    xme_hal_singlyLinkedList_fini(&(*subGraph)->publications);
    xme_hal_singlyLinkedList_fini(&(*subGraph)->subscriptions);
    XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_fini(&(*subGraph)->routes), XME_STATUS_INTERNAL_ERROR);

    xme_hal_mem_free(*subGraph);
    XME_CHECK(0 < xme_hal_singlyLinkedList_removeItem(&xme_core_pnp_lrm_state.graphs,(*subGraph), (bool) false), XME_STATUS_INTERNAL_ERROR);

    return XME_STATUS_SUCCESS;
}

xme_status_t 
xme_core_pnp_lrm_addComponentPortVertex
(
    xme_hal_graph_graph_t* graph, 
    xme_core_pnp_dataLinkGraph_vertexData_t** componentPortVertex
)
{
    xme_hal_graph_vertexId_t newVertex;
    
    XME_CHECK(graph != NULL, XME_STATUS_INVALID_PARAMETER);

    *componentPortVertex = (xme_core_pnp_dataLinkGraph_vertexData_t*)xme_hal_mem_alloc(sizeof(xme_core_pnp_dataLinkGraph_vertexData_t));

    XME_CHECK(*componentPortVertex != NULL, XME_STATUS_OUT_OF_RESOURCES);

    newVertex = xme_hal_graph_addVertex(graph, *componentPortVertex);

    XME_CHECK(newVertex != XME_HAL_GRAPH_INVALID_VERTEX_ID, XME_STATUS_INTERNAL_ERROR);

    (*componentPortVertex)->vertexId = newVertex;
    (*componentPortVertex)->vertexType = XME_CORE_PNP_DATALINKGRAPH_VERTEXTYPE_COMPONENT_PORT;

    return XME_STATUS_SUCCESS;
}

#if 0
xme_status_t
xme_core_pnp_lrm_removeComponentPortVertex
(
    xme_hal_graph_graph_t* graph, 
    xme_hal_graph_vertexId_t vertexId
)
{
    //xme_core_pnp_lrm_vertexData_t* vertexToRemove;

    XME_CHECK(graph != NULL, XME_STATUS_INVALID_PARAMETER);
    XME_CHECK(vertexId != XME_HAL_GRAPH_INVALID_VERTEX_ID, XME_STATUS_INVALID_PARAMETER);
    // FIXME: The following check is not working
    //XME_CHECK(xme_hal_graph_getVertexData(graph, vertexId, &vertexToRemove) == XME_STATUS_SUCCESS, XME_STATUS_INVALID_PARAMETER);
    XME_CHECK(xme_hal_graph_removeVertex(graph, vertexId) == XME_STATUS_SUCCESS, XME_STATUS_INTERNAL_ERROR);
    //xme_hal_mem_free(vertexToRemove);

    //XME_CHECK(vertexToRemove == NULL, XME_STATUS_INTERNAL_ERROR);

    // FIXME: Remove all related edges as well?

    return xme_hal_graph_removeVertex(graph, vertexId);
}
#endif

int
xme_core_pnp_lrm_vertexCompareCallbackSameComponentPort
(
    void* vertexData1,
    void* vertexData2
)
{
    xme_core_pnp_dataLinkGraph_vertexData_t* vd1 = (xme_core_pnp_dataLinkGraph_vertexData_t*) vertexData1;
    xme_core_pnp_dataLinkGraph_vertexData_t* vd2 = (xme_core_pnp_dataLinkGraph_vertexData_t*) vertexData2;

    // This callback only handles component port vertices, everything else does not match
    XME_CHECK(XME_CORE_PNP_DATALINKGRAPH_VERTEXTYPE_COMPONENT_PORT == vd1->vertexType, -1);
    XME_CHECK(XME_CORE_PNP_DATALINKGRAPH_VERTEXTYPE_COMPONENT_PORT == vd2->vertexType, -1);

    return (vd1->nodeId == vd2->nodeId &&
        vd1->topicId == vd2->topicId &&
        vd1->vertexData.componentPortVertex.portType == vd2->vertexData.componentPortVertex.portType &&
        vd1->vertexData.componentPortVertex.componentId == vd2->vertexData.componentPortVertex.componentId &&
        vd1->vertexData.componentPortVertex.announcement->attrSet == vd2->vertexData.componentPortVertex.announcement->attrSet) ?
        0 : 1;
}

int
xme_core_pnp_lrm_edgeCompareCallbackLogicalRouteFilter
(
    void* edgeData1,
    void* edgeData2
)
{
    // FIXME: This function does not compare two edge data, but only one of them. 
    xme_core_pnp_dataLinkGraph_edgeData_t* edgeData;

    XME_ASSERT_RVAL(NULL == edgeData2, -1);

    edgeData = (xme_core_pnp_dataLinkGraph_edgeData_t*) edgeData1;

    return (XME_CORE_PNP_DATALINKGRAPH_EDGETYPE_LOGICALROUTE == edgeData->edgeType) ? 0 : -1;
}

xme_core_pnp_dataLinkGraph_edgeData_t*
xme_core_pnp_lrm_findLogicalRoute
(
    xme_core_pnp_dataLinkGraph_vertexData_t* sourceVertexData,
    xme_core_pnp_dataLinkGraph_vertexData_t* sinkVertexData
)
{
    XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN(xme_core_pnp_lrm_state.graphs, xme_core_pnp_lrm_stateSubGraph_t, subGraph);
    {
        xme_hal_graph_vertexCompareCallback_t vertexCompareCallback;
        xme_hal_graph_edgeCompareCallback_t edgeCompareCallback;
        xme_hal_graph_vertexId_t sourceVertexId;
        xme_hal_graph_vertexId_t sinkVertexId;
        xme_hal_graph_edgeId_t edgeId;
        xme_core_pnp_dataLinkGraph_edgeData_t* edgeData = NULL;

        vertexCompareCallback = xme_hal_graph_getVertexCompareCallback(&subGraph->routes);
        edgeCompareCallback = xme_hal_graph_getEdgeCompareCallback(&subGraph->routes);

        XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_setVertexCompareCallback(&subGraph->routes, xme_core_pnp_lrm_vertexCompareCallbackSameComponentPort), NULL);
        XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_setEdgeCompareCallback(&subGraph->routes, xme_core_pnp_lrm_edgeCompareCallbackLogicalRouteFilter), NULL);

        sourceVertexId = xme_hal_graph_getNextVertexWithDataComparison(&subGraph->routes, sourceVertexData, XME_HAL_GRAPH_INVALID_VERTEX_ID);
        if (XME_HAL_GRAPH_INVALID_VERTEX_ID != sourceVertexId)
        {
            sinkVertexId = xme_hal_graph_getNextVertexWithDataComparison(&subGraph->routes, sinkVertexData, XME_HAL_GRAPH_INVALID_VERTEX_ID);
            if (XME_HAL_GRAPH_INVALID_VERTEX_ID != sinkVertexId)
            {
                edgeId = xme_hal_graph_getNextEdgeBetweenWithDataComparison(&subGraph->routes, sourceVertexId, sinkVertexId, NULL, XME_HAL_GRAPH_INVALID_EDGE_ID);
                if (edgeId != XME_HAL_GRAPH_INVALID_EDGE_ID)
                {
                    XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getEdgeData(&subGraph->routes, edgeId, (void**) &edgeData), NULL);
                    XME_ASSERT_RVAL(NULL != edgeData, NULL);
                    XME_ASSERT_RVAL(XME_CORE_PNP_DATALINKGRAPH_EDGETYPE_LOGICALROUTE == edgeData->edgeType, NULL);

                    // Found a matching edge
                    return edgeData;
                }
            }
        }

        XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_setVertexCompareCallback(&subGraph->routes, vertexCompareCallback), NULL);
        XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_setEdgeCompareCallback(&subGraph->routes, edgeCompareCallback), NULL);

        if (NULL != edgeData)
        {
            return edgeData;
        }
    }
    XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

    // No match found
    return NULL;
}

xme_status_t
xme_core_pnp_lrm_addLogicalRouteEdge
(
    xme_hal_graph_graph_t* graph,
    xme_core_pnp_dataLinkGraph_edgeData_t** logicalRouteEdge,
    xme_hal_graph_vertexId_t sourceVertexId,
    xme_hal_graph_vertexId_t sinkVertexId
)
{
    xme_hal_graph_edgeId_t newEdge;

    XME_CHECK(graph != NULL, XME_STATUS_INVALID_PARAMETER);
    XME_ASSERT(NULL != logicalRouteEdge);

    // FIXME: redundant?
    XME_CHECK(xme_hal_graph_getVertexData(graph, sourceVertexId, NULL) == XME_STATUS_SUCCESS, XME_STATUS_INVALID_PARAMETER);
    XME_CHECK(xme_hal_graph_getVertexData(graph, sinkVertexId, NULL) == XME_STATUS_SUCCESS, XME_STATUS_INVALID_PARAMETER);

    if (NULL == *logicalRouteEdge)
    {
        *logicalRouteEdge = (xme_core_pnp_dataLinkGraph_edgeData_t*)xme_hal_mem_alloc(sizeof(xme_core_pnp_dataLinkGraph_edgeData_t));
        XME_CHECK(*logicalRouteEdge != NULL, XME_STATUS_OUT_OF_RESOURCES);
    }

    XME_ASSERT(NULL != *logicalRouteEdge);

    newEdge = xme_hal_graph_addEdge(graph, sourceVertexId, sinkVertexId, *logicalRouteEdge);

    XME_CHECK_REC(newEdge != XME_HAL_GRAPH_INVALID_EDGE_ID, XME_STATUS_INTERNAL_ERROR, xme_hal_mem_free(*logicalRouteEdge));

    (*logicalRouteEdge)->edgeId = newEdge;
    (*logicalRouteEdge)->edgeType = XME_CORE_PNP_DATALINKGRAPH_EDGETYPE_LOGICALROUTE;

    return XME_STATUS_SUCCESS;
}

xme_status_t 
xme_core_pnp_lrm_removeLogicalRouteEdge
(
    xme_hal_graph_graph_t* graph, 
    xme_hal_graph_edgeId_t edgeId
)
{
    xme_core_pnp_dataLinkGraph_edgeData_t* edgeToRemove;
    
    XME_CHECK(graph != NULL, XME_STATUS_INVALID_PARAMETER);
    XME_CHECK(edgeId != XME_HAL_GRAPH_INVALID_EDGE_ID, XME_STATUS_INVALID_PARAMETER);
    XME_CHECK(xme_hal_graph_getEdgeData(graph, edgeId, (void**)&edgeToRemove) == XME_STATUS_SUCCESS, XME_STATUS_INVALID_PARAMETER);

    XME_CHECK(xme_hal_graph_removeEdge(graph, edgeId) == XME_STATUS_SUCCESS, XME_STATUS_INTERNAL_ERROR);

    xme_hal_mem_free(edgeToRemove);


    XME_CHECK(edgeToRemove != NULL, XME_STATUS_INTERNAL_ERROR);

    return XME_STATUS_SUCCESS;
}

xme_status_t
xme_core_pnp_lrm_addChannelMappingEdge
(
    xme_hal_graph_graph_t* graph,
    xme_core_pnp_dataLinkGraph_edgeData_t** channelMappingEdge,
    xme_hal_graph_vertexId_t sourceVertexId,
    xme_hal_graph_vertexId_t sinkVertexId
)
{
    xme_hal_graph_edgeId_t newChannelMappingEdgeID;

    XME_CHECK(graph != NULL, XME_STATUS_INVALID_PARAMETER);
    XME_ASSERT(NULL != channelMappingEdge);

    XME_CHECK(xme_hal_graph_getVertexData(graph, sourceVertexId, NULL) == XME_STATUS_SUCCESS, XME_STATUS_INVALID_PARAMETER);
    XME_CHECK(xme_hal_graph_getVertexData(graph, sinkVertexId, NULL) == XME_STATUS_SUCCESS, XME_STATUS_INVALID_PARAMETER);

    if (NULL == *channelMappingEdge)
    {
        *channelMappingEdge = (xme_core_pnp_dataLinkGraph_edgeData_t*)xme_hal_mem_alloc(sizeof(xme_core_pnp_dataLinkGraph_edgeData_t));
        XME_CHECK(*channelMappingEdge != NULL, XME_STATUS_OUT_OF_RESOURCES);
    }

    XME_ASSERT(NULL != *channelMappingEdge);

    newChannelMappingEdgeID = xme_hal_graph_addEdge(graph, sourceVertexId, sinkVertexId, *channelMappingEdge);

    XME_CHECK_REC(newChannelMappingEdgeID != XME_HAL_GRAPH_INVALID_EDGE_ID, XME_STATUS_INTERNAL_ERROR, xme_hal_mem_free(*channelMappingEdge));

    (*channelMappingEdge)->edgeId = newChannelMappingEdgeID;
    (*channelMappingEdge)->edgeType = XME_CORE_PNP_DATALINKGRAPH_EDGETYPE_CHANNELMAPPING;

    return XME_STATUS_SUCCESS;
}

#if 0
int 
xme_core_pnp_lrm_compareLogicalRouteEdge
(
    const xme_core_pnp_dataLinkGraph_edgeData_t* const edgeData1, 
    const xme_core_pnp_dataLinkGraph_edgeData_t* const edgeData2
)
{
    XME_CHECK(edgeData1 != NULL, 1);
    XME_CHECK(edgeData2 != NULL, 1);
    //No checks as they would hide null reference exceptions

    if (edgeData1->edgeType == edgeData2->edgeType &&
        edgeData1->edgeType == XME_CORE_PNP_DATALINKGRAPH_EDGETYPE_LOGICALROUTE  && // separate comparison
        edgeData1->edgeData.logicalRouteEdge.topicId == edgeData2->edgeData.logicalRouteEdge.topicId &&
        edgeData1->edgeData.logicalRouteEdge.channelId == edgeData2->edgeData.logicalRouteEdge.channelId)
        return 0;

    return 1;
}
#endif

int 
xme_core_pnp_lrm_compareComponentPortVertex
(
    const xme_core_pnp_dataLinkGraph_vertexData_t* const vertexData1, 
    const xme_core_pnp_dataLinkGraph_vertexData_t* const vertexData2
)
{
    XME_CHECK(vertexData1 != NULL, 1);
    XME_CHECK(vertexData2 != NULL, 1);
    //No checks as they would hide null reference exceptions

    if (vertexData1->vertexType == vertexData2->vertexType &&
        vertexData1->topicId == vertexData2->topicId &&
        vertexData1->nodeId == vertexData2->nodeId &&
        vertexData1->vertexData.componentPortVertex.componentId == vertexData2->vertexData.componentPortVertex.componentId &&
        vertexData1->vertexData.componentPortVertex.componentType == vertexData2->vertexData.componentPortVertex.componentType &&
        vertexData1->vertexData.componentPortVertex.portType == vertexData2->vertexData.componentPortVertex.portType &&
        vertexData1->vertexData.componentPortVertex.portIndex == vertexData2->vertexData.componentPortVertex.portIndex &&
        vertexData1->vertexData.componentPortVertex.lowerConnectionBound == vertexData2->vertexData.componentPortVertex.lowerConnectionBound &&
        vertexData1->vertexData.componentPortVertex.upperConnectionBound == vertexData2->vertexData.componentPortVertex.upperConnectionBound &&
        vertexData1->vertexData.componentPortVertex.announcement->attrSet == vertexData2->vertexData.componentPortVertex.announcement->attrSet)
    {
        return 0;
    } else {
        return 1;
    }
}

// FIXME: Placed here, but this functions should be placed at dataLinkGraph level. 
bool
xme_core_pnp_lrm_isComponentPort
(
    const xme_core_pnp_dataLinkGraph_vertexData_t* const vertexData
)
{
    if (XME_CORE_PNP_DATALINKGRAPH_VERTEXTYPE_COMPONENT_PORT == vertexData->vertexType)
    {
        return true;
    }
    else
    {
        return false;
    }
}

// FIXME: Placed here, but this functions should be placed at dataLinkGraph level. 
bool
xme_core_pnp_lrm_isComponentPortPublication
(
    const xme_core_pnp_dataLinkGraph_vertexData_t* const vertexData
)
{
    if (XME_CORE_COMPONENT_PORTTYPE_DCC_PUBLICATION == vertexData->vertexData.componentPortVertex.portType ||
        XME_CORE_COMPONENT_PORTTYPE_RR_REQUEST_SENDER == vertexData->vertexData.componentPortVertex.portType ||
        XME_CORE_COMPONENT_PORTTYPE_RR_RESPONSE_SENDER == vertexData->vertexData.componentPortVertex.portType)
    {
        return true;
    }
    else
    {
        return false;
    }
}

// FIXME: Placed here, but this functions should be placed at dataLinkGraph level. 
bool
xme_core_pnp_lrm_isComponentPortSubscription
(
    const xme_core_pnp_dataLinkGraph_vertexData_t* const vertexData
)
{
    if (XME_CORE_COMPONENT_PORTTYPE_DCC_SUBSCRIPTION == vertexData->vertexData.componentPortVertex.portType ||
        XME_CORE_COMPONENT_PORTTYPE_RR_REQUEST_HANDLER == vertexData->vertexData.componentPortVertex.portType ||
        XME_CORE_COMPONENT_PORTTYPE_RR_RESPONSE_HANDLER == vertexData->vertexData.componentPortVertex.portType)
    {
        return true;
    }
    else
    {
        return false;
    }
}

#if 0
bool 
xme_core_pnp_lrm_announcementExists
(
    xme_hal_linkedList_descriptor_t announcementList, 
    const xme_core_pnp_lrm_announcement_t* const announcement 
)
{
    bool result = false;

    // Equality of announcements is based on all properties except transactionId
    // and topic id, because input announcementList is already of the same
    // topic id.

    XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN(
        announcementList, 
        xme_core_pnp_lrm_announcement_t, 
        announcementItem
    );
    {
        if (announcementItem->nodeId == announcement->nodeId &&
            announcementItem->componentId == announcement->componentId &&
            announcementItem->portType == announcement->portType)
        {
            result = true;
            break;
        }
    }
    XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

    return result;
}
#endif

bool 
xme_core_pnp_lrm_matchAnnouncement
(
    xme_core_pnp_lrm_announcement_t* sourceAnnouncement, 
    xme_core_pnp_lrm_announcement_t* sinkAnnouncement
)
{
    XME_CHECK(sourceAnnouncement != NULL, false);
    XME_CHECK(sinkAnnouncement != NULL, false);

    return sourceAnnouncement->topicId == sinkAnnouncement->topicId &&
        xme_core_pnp_lrm_matchAttributes(sourceAnnouncement, sinkAnnouncement);
}

bool
xme_core_pnp_lrm_matchAttributes
(
    const xme_core_pnp_lrm_announcement_t* const sourceAnnouncement,
    const xme_core_pnp_lrm_announcement_t* const sinkAnnouncement
)
{
    xme_status_t status;
    int matches = false;

    status = xme_core_directory_attribute_isFilterMatchingDefinition
    (
        sinkAnnouncement->attrSet,
        sourceAnnouncement->attrSet,
        &matches
    );
    XME_ASSERT(XME_STATUS_SUCCESS == status);

    return (bool) matches;
}

xme_status_t
xme_core_pnp_lrm_createChannelMappings
(
    xme_core_pnp_lrm_logicalRoutes_t* routes
)
{
    XME_CHECK(NULL != routes, XME_STATUS_INTERNAL_ERROR);

    XME_HAL_SINGLYLINKEDLIST_INIT(temporaryMapping);

    // FIRST STEP: Explore the logical routes looking for possible requests and responses. 

    // Iterate over logical routes
    XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_initVertexIterator(routes), XME_STATUS_INTERNAL_ERROR);
    while (xme_hal_graph_hasNextVertex(routes))
    {
        xme_core_pnp_dataLinkGraph_vertexData_t* sourceVertex;

        xme_hal_graph_vertexId_t sourceVertexId = xme_hal_graph_nextVertex(routes);
        XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getVertexData(routes, sourceVertexId, (void**) &sourceVertex), XME_STATUS_INTERNAL_ERROR);

        XME_ASSERT(XME_CORE_PNP_DATALINKGRAPH_VERTEXTYPE_COMPONENT_PORT == sourceVertex->vertexType);

        if (XME_CORE_COMPONENT_PORTTYPE_RR_REQUEST_SENDER == sourceVertex->vertexData.componentPortVertex.portType)
        {
            // Lookup for all instances of RR_REQUEST_HANDLER associated to this vertex. 
            XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_initOutgoingEdgeIterator(routes, sourceVertexId), XME_STATUS_INTERNAL_ERROR);
            while(xme_hal_graph_hasNextOutgoingEdge(routes, sourceVertexId))
            {
                xme_core_pnp_dataLinkGraph_edgeData_t* edgeData = NULL;
                xme_hal_graph_vertexId_t sinkVertexId;
                xme_core_pnp_dataLinkGraph_vertexData_t* sinkVertex;
                bool found;

                xme_hal_graph_edgeId_t tempEdgeId = xme_hal_graph_nextOutgoingEdge(routes, sourceVertexId);
                XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getEdgeData(routes, tempEdgeId, (void**) &edgeData), XME_STATUS_INTERNAL_ERROR);

                XME_ASSERT(XME_CORE_PNP_DATALINKGRAPH_EDGETYPE_LOGICALROUTE == edgeData->edgeType);

                sinkVertexId = xme_hal_graph_getSinkVertex(routes, tempEdgeId);
                XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getVertexData(routes, sinkVertexId, (void**) &sinkVertex), XME_STATUS_INTERNAL_ERROR);

                // Make some safety assertions. 
                XME_ASSERT(XME_CORE_PNP_DATALINKGRAPH_VERTEXTYPE_COMPONENT_PORT == sinkVertex->vertexType);
                XME_ASSERT(XME_CORE_COMPONENT_PORTTYPE_RR_REQUEST_HANDLER == sinkVertex->vertexData.componentPortVertex.portType);

                // Check if there is at least one element in the temporaryMapping matching the criteria:
                // - Same nodeID.
                // - Same componentID.
                found = false;
                XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN(temporaryMapping, xme_core_pnp_lrm_requestResponseMapping_t, mappingItem);
                {
                    XME_CHECK(NULL != mappingItem, XME_STATUS_INTERNAL_ERROR);

                    // Two different possibilities.
                    // - The data matches with an existing response mapping data (nodeID and componentID) -> include in the entry the information of the request. 
                    // - Exactly the same request data -> internal_error
                    if (mappingItem->requestResponseComponentID == sinkVertex->vertexData.componentPortVertex.componentId &&
                        mappingItem->requestResponseNodeID == sinkVertex->nodeId &&
                        mappingItem->responseHandlerComponentID == sourceVertex->vertexData.componentPortVertex.componentId &&
                        mappingItem->responseHandlerNodeID == sourceVertex->nodeId) 
                    {
                        mappingItem->requestChannelID = edgeData->edgeData.logicalRouteEdge.channelId;
                        mappingItem->requestHandlerVertexID = sinkVertexId;
                        mappingItem->requestSenderComponentID = sourceVertex->vertexData.componentPortVertex.componentId;
                        mappingItem->requestSenderNodeID = sourceVertex->nodeId;
                        found = true; // To notify that we have already find a mapping item. 
                    }
                    else if (mappingItem->requestChannelID == edgeData->edgeData.logicalRouteEdge.channelId &&
                        mappingItem->requestHandlerVertexID == sinkVertexId &&
                        mappingItem->requestResponseNodeID == sinkVertex->nodeId && 
                        mappingItem->requestResponseComponentID == sinkVertex->vertexData.componentPortVertex.componentId)
                    {
                        return XME_STATUS_INTERNAL_ERROR;
                    }
                }
                XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

                if (!found)
                {
                    // Add a new item to the temporary mapping list. This is due the route iterator is already in use, so we cannot use it in this step. 
                    xme_core_pnp_lrm_requestResponseMapping_t* requestResponseMappingItem = NULL;

                    requestResponseMappingItem = (xme_core_pnp_lrm_requestResponseMapping_t*) xme_hal_mem_alloc(sizeof(xme_core_pnp_lrm_requestResponseMapping_t));
                    XME_ASSERT(NULL != requestResponseMappingItem);
                    requestResponseMappingItem->requestChannelID = edgeData->edgeData.logicalRouteEdge.channelId;
                    requestResponseMappingItem->requestHandlerVertexID = sinkVertexId;
                    requestResponseMappingItem->requestSenderNodeID = sourceVertex->nodeId;
                    requestResponseMappingItem->requestSenderComponentID = sourceVertex->vertexData.componentPortVertex.componentId;
                    requestResponseMappingItem->requestResponseNodeID = sinkVertex->nodeId;
                    requestResponseMappingItem->requestResponseComponentID = sinkVertex->vertexData.componentPortVertex.componentId;

                    XME_CHECK(XME_STATUS_SUCCESS == xme_hal_singlyLinkedList_addItem(&temporaryMapping, requestResponseMappingItem), XME_STATUS_INTERNAL_ERROR);
                }
            }
            XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_finiOutgoingEdgeIterator(routes, sourceVertexId), XME_STATUS_INTERNAL_ERROR);
        }
        else if (XME_CORE_COMPONENT_PORTTYPE_RR_RESPONSE_SENDER == sourceVertex->vertexData.componentPortVertex.portType)
        {
            // Lookup for all instances of RR_REQUEST_HANDLER associated to this vertex. 
            XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_initOutgoingEdgeIterator(routes, sourceVertexId), XME_STATUS_INTERNAL_ERROR);
            while(xme_hal_graph_hasNextOutgoingEdge(routes, sourceVertexId))
            {
                xme_core_pnp_dataLinkGraph_edgeData_t* edgeData = NULL;
                xme_hal_graph_vertexId_t sinkVertexId;
                xme_core_pnp_dataLinkGraph_vertexData_t* sinkVertex;
                bool found;

                xme_hal_graph_edgeId_t tempEdgeId = xme_hal_graph_nextOutgoingEdge(routes, sourceVertexId);
                XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getEdgeData(routes, tempEdgeId, (void**) &edgeData), XME_STATUS_INTERNAL_ERROR);

                XME_ASSERT(XME_CORE_PNP_DATALINKGRAPH_EDGETYPE_LOGICALROUTE == edgeData->edgeType);

                sinkVertexId = xme_hal_graph_getSinkVertex(routes, tempEdgeId);
                XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getVertexData(routes, sinkVertexId, (void**) &sinkVertex), XME_STATUS_INTERNAL_ERROR);

                // Make some safety assertions. 
                XME_ASSERT(XME_CORE_PNP_DATALINKGRAPH_VERTEXTYPE_COMPONENT_PORT == sinkVertex->vertexType);
                XME_ASSERT(XME_CORE_COMPONENT_PORTTYPE_RR_RESPONSE_HANDLER == sinkVertex->vertexData.componentPortVertex.portType);

                // Check if there is at least one element in the temporaryMapping matching the criteria:
                // - Same nodeID.
                // - Same componentID.
                found = false;
                XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN(temporaryMapping, xme_core_pnp_lrm_requestResponseMapping_t, mappingItem);
                {
                    XME_CHECK(NULL != mappingItem, XME_STATUS_INTERNAL_ERROR);

                    // Two different possibilities.
                    // - The data matches with an existing request mapping data (nodeID and componentID) -> include in the entry the information of the response. 
                    // - Exactly the same response data -> internal_error
                    if (mappingItem->requestResponseComponentID == sourceVertex->vertexData.componentPortVertex.componentId &&
                        mappingItem->requestResponseNodeID == sourceVertex->nodeId &&
                        mappingItem->requestSenderComponentID == sinkVertex->vertexData.componentPortVertex.componentId &&
                        mappingItem->requestSenderNodeID == sinkVertex->nodeId) 
                    {
                        mappingItem->responseChannelID = edgeData->edgeData.logicalRouteEdge.channelId;
                        mappingItem->responseSenderVertexID = sourceVertexId;
                        mappingItem->responseHandlerComponentID = sinkVertex->vertexData.componentPortVertex.componentId;
                        mappingItem->responseHandlerNodeID = sinkVertex->nodeId;

                        found = true; // To notify that we have already find a mapping item. 
                    }
                    else if (mappingItem->responseChannelID == edgeData->edgeData.logicalRouteEdge.channelId &&
                        mappingItem->responseSenderVertexID == sourceVertexId &&
                        mappingItem->requestResponseNodeID == sourceVertex->nodeId && 
                        mappingItem->requestResponseComponentID == sourceVertex->vertexData.componentPortVertex.componentId)
                    {
                        return XME_STATUS_INTERNAL_ERROR;
                    }
                }
                XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

                if (!found)
                {
                    // Add a new item to the temporary mapping list. This is due the route iterator is already in use, so we cannot use it in this step. 
                    xme_core_pnp_lrm_requestResponseMapping_t* requestResponseMappingItem = NULL;

                    requestResponseMappingItem = (xme_core_pnp_lrm_requestResponseMapping_t*) xme_hal_mem_alloc(sizeof(xme_core_pnp_lrm_requestResponseMapping_t));
                    XME_ASSERT(NULL != requestResponseMappingItem);
                    requestResponseMappingItem->responseChannelID = edgeData->edgeData.logicalRouteEdge.channelId;
                    requestResponseMappingItem->responseSenderVertexID = sourceVertexId;
                    requestResponseMappingItem->requestResponseNodeID = sourceVertex->nodeId;
                    requestResponseMappingItem->requestResponseComponentID = sourceVertex->vertexData.componentPortVertex.componentId;
                    requestResponseMappingItem->responseHandlerNodeID = sinkVertex->nodeId;
                    requestResponseMappingItem->responseHandlerComponentID = sinkVertex->vertexData.componentPortVertex.componentId;

                    XME_CHECK(XME_STATUS_SUCCESS == xme_hal_singlyLinkedList_addItem(&temporaryMapping, requestResponseMappingItem), XME_STATUS_INTERNAL_ERROR);
                }
            }
            XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_finiOutgoingEdgeIterator(routes, sourceVertexId), XME_STATUS_INTERNAL_ERROR);
        }
    }
    XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_finiVertexIterator(routes), XME_STATUS_INTERNAL_ERROR);

    // SECOND STEP: Add channel mappings, and remove orphan requests/responses. 

    // Iterate over the temporaryMapping
    XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN(temporaryMapping, xme_core_pnp_lrm_requestResponseMapping_t, mappingItem);
    {
        XME_CHECK(NULL != mappingItem, XME_STATUS_INTERNAL_ERROR);

        // Three different cases:
        // - No response for a request -> remove the item from logical routes. 
        // - No request for a response -> remove the item from logical routes. 
        // - Both request and response are included in the mappingItem -> add the channelMapping.
        if (XME_CORE_INVALID_CHANNEL_ID == mappingItem->responseChannelID && // ||?
            XME_HAL_GRAPH_INVALID_VERTEX_ID == mappingItem->responseSenderVertexID)
        {
            // The response does not have a counterpart. So, remove the request from logical routes. 

            // For removing the logical route, we have to check both requestHandlerVertexId and channelId.
            XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_initVertexIterator(routes), XME_STATUS_INTERNAL_ERROR);
            while (xme_hal_graph_hasNextVertex(routes))
            {
                xme_core_pnp_dataLinkGraph_edgeData_t* edgeData = NULL;

                xme_hal_graph_vertexId_t requestHandlerVertexID = xme_hal_graph_nextVertex(routes);

                if (requestHandlerVertexID == mappingItem->requestHandlerVertexID)
                {
                    // Only in this case, we are located in the right node. 
                    // Iterate for incoming edges. 
                    XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_initIncomingEdgeIterator(routes, requestHandlerVertexID), XME_STATUS_INTERNAL_ERROR);
                    while(xme_hal_graph_hasNextIncomingEdge(routes, requestHandlerVertexID))
                    {
                        xme_hal_graph_edgeId_t edgeId;

                        edgeId = xme_hal_graph_nextIncomingEdge(routes, requestHandlerVertexID);

                        XME_CHECK(XME_HAL_GRAPH_INVALID_EDGE_ID != edgeId, XME_STATUS_INTERNAL_ERROR);

                        XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getEdgeData(routes, edgeId, (void**) &edgeData), XME_STATUS_INTERNAL_ERROR);
                        XME_ASSERT(XME_CORE_PNP_DATALINKGRAPH_EDGETYPE_LOGICALROUTE == edgeData->edgeType);

                        if (edgeData->edgeData.logicalRouteEdge.channelId == mappingItem->requestChannelID)
                        {
                            // This is the edge to remove!
                            XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_removeEdge(routes, edgeId), XME_STATUS_INTERNAL_ERROR);
                        }
                    }
                    XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_finiIncomingEdgeIterator(routes, requestHandlerVertexID), XME_STATUS_INTERNAL_ERROR);
                }
            }
            XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_finiVertexIterator(routes), XME_STATUS_INTERNAL_ERROR);
        }
        else if (XME_CORE_INVALID_CHANNEL_ID == mappingItem->requestChannelID && // ||?
            XME_HAL_GRAPH_INVALID_VERTEX_ID == mappingItem->requestHandlerVertexID)
        {
            // The request does not have a counterpart. So, remove the response from logical routes. 

            // For removing the logical route, we have to check both responseSenderVertexId and channelId.
            XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_initVertexIterator(routes), XME_STATUS_INTERNAL_ERROR);
            while (xme_hal_graph_hasNextVertex(routes))
            {
                xme_core_pnp_dataLinkGraph_edgeData_t* edgeData = NULL;

                xme_hal_graph_vertexId_t responseSenderVertexID = xme_hal_graph_nextVertex(routes);

                if (responseSenderVertexID == mappingItem->responseSenderVertexID)
                {
                    // Only in this case, we are located in the right node. 
                    // Iterate for outgoing edges. 
                    XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_initOutgoingEdgeIterator(routes, responseSenderVertexID), XME_STATUS_INTERNAL_ERROR);
                    while(xme_hal_graph_hasNextOutgoingEdge(routes, responseSenderVertexID))
                    {
                        xme_hal_graph_edgeId_t edgeId;

                        edgeId = xme_hal_graph_nextOutgoingEdge(routes, responseSenderVertexID);

                        XME_CHECK(XME_HAL_GRAPH_INVALID_EDGE_ID != edgeId, XME_STATUS_INTERNAL_ERROR);

                        XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_getEdgeData(routes, edgeId, (void**) &edgeData), XME_STATUS_INTERNAL_ERROR);
                        XME_ASSERT(XME_CORE_PNP_DATALINKGRAPH_EDGETYPE_LOGICALROUTE == edgeData->edgeType);

                        if (edgeData->edgeData.logicalRouteEdge.channelId == mappingItem->responseChannelID)
                        {
                            // This is the edge to remove!
                            XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_removeEdge(routes, edgeId), XME_STATUS_INTERNAL_ERROR);
                        }
                    }
                    XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_finiIncomingEdgeIterator(routes, responseSenderVertexID), XME_STATUS_INTERNAL_ERROR);
                }
            }
            XME_CHECK(XME_STATUS_SUCCESS == xme_hal_graph_finiVertexIterator(routes), XME_STATUS_INTERNAL_ERROR);
        }
        else
        {
            // We do have everything we need for creating the channel mapping. Let's include that in the logical route. 
            xme_core_pnp_dataLinkGraph_edgeData_t* channelMappingEdge = NULL;

            // Create the edge between requestHandlerVertex and responseSenderVertex. 
            XME_CHECK
            (
                XME_STATUS_SUCCESS == xme_core_pnp_lrm_addChannelMappingEdge
                (
                    routes, 
                    &channelMappingEdge, 
                    mappingItem->requestHandlerVertexID, 
                    mappingItem->responseSenderVertexID
                ), 
                    XME_STATUS_INTERNAL_ERROR
            );

            channelMappingEdge->edgeData.channelMappingEdge.sourceChannelId = mappingItem->requestChannelID;
            channelMappingEdge->edgeData.channelMappingEdge.destChannelId = mappingItem->responseChannelID;
        }
    }
    XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

    return XME_STATUS_SUCCESS;
}


xme_status_t
xme_core_pnp_lrm_getAssociatedVertices
(
    xme_core_pnp_lrm_announcement_t* const announcement,
    xme_core_pnp_lrm_logicalRoutes_t* outSubgraph
)
{
    xme_status_t status;
    xme_core_pnp_dataLinkGraph_vertexData_t* routeVertexData;
    xme_hal_graph_vertexId_t vertexID;
    xme_hal_graph_edgeId_t edgeID;
    xme_core_pnp_lrm_stateSubGraph_t* subGraph = NULL;

    // Check the incoming data
    XME_CHECK(NULL != outSubgraph, XME_STATUS_INVALID_PARAMETER);
    XME_CHECK(NULL != announcement, XME_STATUS_INVALID_PARAMETER);

    // Get the corresponding subgraph associated to the topic.
    XME_HAL_SINGLYLINKEDLIST_ITERATE_BEGIN(
        xme_core_pnp_lrm_state.graphs,
        xme_core_pnp_lrm_stateSubGraph_t,
        stateSubgraph);
    {
        if (stateSubgraph->topicId == announcement->topicId)
        {
            subGraph = stateSubgraph;
            break;
        }
    }
    XME_HAL_SINGLYLINKEDLIST_ITERATE_END();

    // If the graph is not found,
    XME_CHECK(NULL != subGraph, XME_STATUS_NOT_FOUND);

    status = xme_hal_graph_initVertexIterator(&subGraph->routes);
    XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);

    while(xme_hal_graph_hasNextVertex(&subGraph->routes))
    {
        vertexID = xme_hal_graph_nextVertex(&subGraph->routes);
        XME_CHECK(XME_HAL_GRAPH_INVALID_VERTEX_ID != vertexID, XME_STATUS_INTERNAL_ERROR);
        // Get vertexData
        status = xme_hal_graph_getVertexData(&subGraph->routes, vertexID, (void**)&routeVertexData);
        XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);

        // Check if this is the vertex that we are looking for.
        // Note that vertexID should not be compared, because the data is received from an external soure.
        // Additionally, topicID is previously checked when getting the routes.
        if (routeVertexData->nodeId == announcement->nodeId &&
            routeVertexData->vertexType == XME_CORE_PNP_DATALINKGRAPH_VERTEXTYPE_COMPONENT_PORT &&
            routeVertexData->vertexData.componentPortVertex.componentId == announcement->componentId &&
            routeVertexData->vertexData.componentPortVertex.portIndex == announcement->portIndex &&
            routeVertexData->vertexData.componentPortVertex.portType == announcement->portType)
        {
            xme_core_pnp_dataLinkGraph_vertexData_t* outVertexData;

            status = xme_core_pnp_lrm_addComponentPortVertex(outSubgraph, &outVertexData);
            XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);

            outVertexData->nodeId = announcement->nodeId;
            outVertexData->topicId = announcement->topicId;
            outVertexData->vertexData.componentPortVertex.componentId = announcement->componentId;
            outVertexData->vertexData.componentPortVertex.portType = announcement->portType;
            outVertexData->vertexData.componentPortVertex.portIndex = announcement->portIndex;
            outVertexData->vertexData.componentPortVertex.lowerConnectionBound = announcement->lowerConnectionBound;
            outVertexData->vertexData.componentPortVertex.upperConnectionBound = announcement->upperConnectionBound;
            outVertexData->vertexData.componentPortVertex.announcement = announcement;

            // Check if this vertex is publisher or subscriber.
            if (xme_core_pnp_lrm_isComponentPortPublication(routeVertexData))
            {
                xme_hal_graph_vertexId_t sinkVertexID;

                // If the vertex is a publication, then get outgoing edges.
                status = xme_hal_graph_initOutgoingEdgeIterator(&subGraph->routes, vertexID);
                XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);

                while(xme_hal_graph_hasNextOutgoingEdge(&subGraph->routes, vertexID))
                {
                    xme_core_pnp_dataLinkGraph_vertexData_t* sinkVertexData;
                    xme_core_pnp_dataLinkGraph_vertexData_t* subscriptionVertexData;
                    xme_core_pnp_dataLinkGraph_edgeData_t* edgeData = NULL;
                    xme_core_channelId_t channelID;

                    edgeID = xme_hal_graph_nextOutgoingEdge(&subGraph->routes, vertexID);
                    XME_CHECK(XME_HAL_GRAPH_INVALID_EDGE_ID != edgeID, XME_STATUS_INTERNAL_ERROR);

                    // Get the target sink vertex.
                    sinkVertexID = xme_hal_graph_getSinkVertex(&subGraph->routes, edgeID);
                    XME_CHECK(XME_HAL_GRAPH_INVALID_VERTEX_ID != sinkVertexID, XME_STATUS_INTERNAL_ERROR);

                    status = xme_hal_graph_getVertexData(&subGraph->routes, sinkVertexID, (void**)&sinkVertexData);
                    XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);

                    // Add the sink vertex to outgoing routes.
                    status = xme_core_pnp_lrm_addComponentPortVertex(outSubgraph, &subscriptionVertexData);
                    XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);

                    subscriptionVertexData->nodeId = sinkVertexData->nodeId;
                    subscriptionVertexData->topicId = sinkVertexData->topicId;
                    subscriptionVertexData->vertexData.componentPortVertex.componentId = sinkVertexData->vertexData.componentPortVertex.componentId;
                    subscriptionVertexData->vertexData.componentPortVertex.portType = sinkVertexData->vertexData.componentPortVertex.portType;
                    subscriptionVertexData->vertexData.componentPortVertex.portIndex = sinkVertexData->vertexData.componentPortVertex.portIndex;
                    subscriptionVertexData->vertexData.componentPortVertex.lowerConnectionBound = sinkVertexData->vertexData.componentPortVertex.lowerConnectionBound;
                    subscriptionVertexData->vertexData.componentPortVertex.upperConnectionBound = sinkVertexData->vertexData.componentPortVertex.upperConnectionBound;
                    subscriptionVertexData->vertexData.componentPortVertex.announcement = sinkVertexData->vertexData.componentPortVertex.announcement;

                    // Add the edge to outgoing routes.
                    XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_addLogicalRouteEdge(outSubgraph, &edgeData, outVertexData->vertexId, subscriptionVertexData->vertexId), XME_STATUS_INTERNAL_ERROR);

                    XME_CHECK(NULL != edgeData, XME_STATUS_OUT_OF_RESOURCES);

                    XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_generateChannelID(outVertexData->vertexData.componentPortVertex.announcement->transactionId, &channelID), XME_STATUS_INTERNAL_ERROR);

                    edgeData->edgeData.logicalRouteEdge.topicId = outVertexData->topicId;
                    edgeData->edgeData.logicalRouteEdge.channelId = channelID;
                }

                status = xme_hal_graph_finiOutgoingEdgeIterator(&subGraph->routes, vertexID);
                XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);
            }
            else if (xme_core_pnp_lrm_isComponentPortSubscription(routeVertexData))
            {
                // If the vertex is a subscription, get incoming edges.
                xme_hal_graph_vertexId_t sourceVertexID;

                status = xme_hal_graph_initIncomingEdgeIterator(&subGraph->routes, vertexID);
                XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);

                while(xme_hal_graph_hasNextIncomingEdge(&subGraph->routes, vertexID))
                {
                    xme_core_pnp_dataLinkGraph_vertexData_t* sourceVertexData;
                    xme_core_pnp_dataLinkGraph_vertexData_t* publicationVertexData;
                    xme_core_pnp_dataLinkGraph_edgeData_t* edgeData = NULL;
                    xme_core_channelId_t channelID;

                    edgeID = xme_hal_graph_nextIncomingEdge(&subGraph->routes, vertexID);
                    XME_CHECK(XME_HAL_GRAPH_INVALID_EDGE_ID != edgeID, XME_STATUS_INTERNAL_ERROR);

                    // Get the target source vertex.
                    sourceVertexID = xme_hal_graph_getSourceVertex(&subGraph->routes, edgeID);
                    XME_CHECK(XME_HAL_GRAPH_INVALID_VERTEX_ID != sourceVertexID, XME_STATUS_INTERNAL_ERROR);

                    status = xme_hal_graph_getVertexData(&subGraph->routes, sourceVertexID, (void**)&sourceVertexData);
                    XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);

                    // Add the sink vertex to outgoing routes.
                    status = xme_core_pnp_lrm_addComponentPortVertex(outSubgraph, &publicationVertexData);
                    XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);

                    publicationVertexData->nodeId = sourceVertexData->nodeId;
                    publicationVertexData->topicId = sourceVertexData->topicId;
                    publicationVertexData->vertexData.componentPortVertex.componentId = sourceVertexData->vertexData.componentPortVertex.componentId;
                    publicationVertexData->vertexData.componentPortVertex.portType = sourceVertexData->vertexData.componentPortVertex.portType;
                    publicationVertexData->vertexData.componentPortVertex.portIndex = sourceVertexData->vertexData.componentPortVertex.portIndex;
                    publicationVertexData->vertexData.componentPortVertex.lowerConnectionBound = sourceVertexData->vertexData.componentPortVertex.lowerConnectionBound;
                    publicationVertexData->vertexData.componentPortVertex.upperConnectionBound = sourceVertexData->vertexData.componentPortVertex.upperConnectionBound;
                    publicationVertexData->vertexData.componentPortVertex.announcement = sourceVertexData->vertexData.componentPortVertex.announcement;

                    // Add the edge to outgoing routes.
                    XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_addLogicalRouteEdge(outSubgraph, &edgeData, publicationVertexData->vertexId, outVertexData->vertexId), XME_STATUS_INTERNAL_ERROR);

                    XME_CHECK(NULL != edgeData, XME_STATUS_OUT_OF_RESOURCES);

                    XME_CHECK(XME_STATUS_SUCCESS == xme_core_pnp_lrm_generateChannelID(publicationVertexData->vertexData.componentPortVertex.announcement->transactionId, &channelID), XME_STATUS_INTERNAL_ERROR);

                    edgeData->edgeData.logicalRouteEdge.topicId = outVertexData->topicId;
                    edgeData->edgeData.logicalRouteEdge.channelId = channelID;
                }

                status = xme_hal_graph_finiIncomingEdgeIterator(&subGraph->routes, vertexID);
                XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);
            }
            else
            {
                // The data of provided vertexData does not correspond to any of the expected use cases.
                return XME_STATUS_INTERNAL_ERROR;
            }
        }
    }
    status = xme_hal_graph_finiVertexIterator(&subGraph->routes);
    XME_CHECK(XME_STATUS_SUCCESS == status, XME_STATUS_INTERNAL_ERROR);

    // Return corresponding output values.
    return XME_STATUS_SUCCESS;
}

/**
 * @}
 */
